aiobungie

A statically typed, asynchronous API wrapper for building clients for Bungie's API in Python.

Getting Started

This is the basic client you probably want you start with.

import aiobungie

client = aiobungie.Client('YOUR_API_KEY', client_secret='KEY', client_id=0)

async def main() -> None:
    async with client.rest:
        # Search for Destiny 2 memberships.
        users = await client.search_users('Crit')

        # Iterate over the users and take the first 5 results.
        for user in users.take(5):
            # Print the user name and their code.
            print(f'{user.name} {user.code}')

# aiobungie provides an internal function to run async functions.
# It's equivalent to asyncio.run()
client.run(main()) # or asyncio.run(main())

RESTClient

aiobungie provides a second way to use Bungie's API,

a single RESTClient allows you to make requests and return JSON objects immediately.

This bypasses the need to deserialize and create objects. It also exposes all OAuth2 and manifest methods. This can be faster for REST apis.

This is considered the core client since aiobungie.Client is built on top of it. Using the aiobungie.rest property allows direct access to the raw REST client instance.

import aiobungie
import asyncio

client = aiobungie.RESTClient("TOKEN")

async def main() -> None:
    async with client as rest:
        payload = await rest.fetch_membership('Fate怒', 4275)

        for membership in payload:
            print(membership['membershipId'])

asyncio.run(main())

RESTPool

A REST client pool allows you to acquire multiple RESTClient that share the same state.

This is useful when you want to spawn an instance for each client which shared the same state.

import aiobungie
import asyncio

pool = aiobungie.RESTPool("token")

async def set() -> None:
    # Set your ID to access it from other places.
    pool.metadata['my_id'] = 4401
    async with pool.acquire() as instance:
        ...

async def fetch() -> None:
    my_id: int = pool.metadata['my_id']
    async with pool.acquire() as instance: # A different client instance.
        my_user = instance.fetch_bungie_user(my_id)

await asyncio.gather(set(), fetch())

When should you use which client?

  • Use Client when you want to build a Chat Bot, Discord Bot, access data as Python classes.
  • Use RESTClient when you want one TCP session for all clients, access data as JSON payloads.
  • Use RESTPool when you're serving a large amount of connections and want to spawn a session for each, access data as JSON payloads. Note that setting up multiple TCP connections can be expensive.
  1# MIT License
  2#
  3# Copyright (c) 2020 - Present nxtlo
  4#
  5# Permission is hereby granted, free of charge, to any person obtaining a copy
  6# of this software and associated documentation files (the "Software"), to deal
  7# in the Software without restriction, including without limitation the rights
  8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9# copies of the Software, and to permit persons to whom the Software is
 10# furnished to do so, subject to the following conditions:
 11#
 12# The above copyright notice and this permission notice shall be included in all
 13# copies or substantial portions of the Software.
 14#
 15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 21# SOFTWARE.
 22
 23"""A statically typed, asynchronous API wrapper for building clients for Bungie's API in Python.
 24
 25### Getting Started
 26
 27This is the basic client you probably want you start with.
 28
 29```py
 30import aiobungie
 31
 32client = aiobungie.Client('YOUR_API_KEY', client_secret='KEY', client_id=0)
 33
 34async def main() -> None:
 35    async with client.rest:
 36        # Search for Destiny 2 memberships.
 37        users = await client.search_users('Crit')
 38
 39        # Iterate over the users and take the first 5 results.
 40        for user in users.take(5):
 41            # Print the user name and their code.
 42            print(f'{user.name} {user.code}')
 43
 44# aiobungie provides an internal function to run async functions.
 45# It's equivalent to asyncio.run()
 46client.run(main()) # or asyncio.run(main())
 47```
 48
 49### RESTClient
 50
 51aiobungie provides a second way to use Bungie's API,
 52
 53a single `RESTClient` allows you to make requests and return JSON objects immediately.
 54
 55This bypasses the need to deserialize and create objects. It also exposes all `OAuth2` and `manifest` methods.
 56This can be faster for `REST` apis.
 57
 58This is considered the core client since `aiobungie.Client` is built on top of it.
 59Using the `.rest` property allows direct access to the raw REST client instance.
 60
 61
 62```py
 63import aiobungie
 64import asyncio
 65
 66client = aiobungie.RESTClient("TOKEN")
 67
 68async def main() -> None:
 69    async with client as rest:
 70        payload = await rest.fetch_membership('Fate怒', 4275)
 71
 72        for membership in payload:
 73            print(membership['membershipId'])
 74
 75asyncio.run(main())
 76```
 77
 78### RESTPool
 79
 80A REST client pool allows you to acquire multiple `RESTClient` that share the same state.
 81
 82This is useful when you want to spawn an instance for each client which shared the same state.
 83
 84```py
 85import aiobungie
 86import asyncio
 87
 88pool = aiobungie.RESTPool("token")
 89
 90async def set() -> None:
 91    # Set your ID to access it from other places.
 92    pool.metadata['my_id'] = 4401
 93    async with pool.acquire() as instance:
 94        ...
 95
 96async def fetch() -> None:
 97    my_id: int = pool.metadata['my_id']
 98    async with pool.acquire() as instance: # A different client instance.
 99        my_user = instance.fetch_bungie_user(my_id)
100
101await asyncio.gather(set(), fetch())
102```
103
104### When should you use which client?
105* Use `Client` when you want to build a Chat Bot, Discord Bot, access data as Python classes.
106* Use `RESTClient` when you want one TCP session for all clients, access data as JSON payloads.
107* Use `RESTPool` when you're serving a large amount of connections and want to spawn a session for each,
108access data as JSON payloads.
109Note that setting up multiple TCP connections can be expensive.
110"""
111
112from __future__ import annotations
113
114from aiobungie import builders
115from aiobungie import crates
116from aiobungie import interfaces
117from aiobungie import traits
118from aiobungie import typedefs
119from aiobungie import url
120from aiobungie.client import Client
121from aiobungie.error import *
122from aiobungie.internal import iterators
123from aiobungie.internal.assets import Image
124from aiobungie.internal.enums import *
125from aiobungie.internal.factory import EmptyFactory
126from aiobungie.internal.factory import Factory
127from aiobungie.internal.iterators import *
128from aiobungie.rest import *
129
130# Activity enums
131from .crates.activity import Difficulty
132
133# Components enums
134from .crates.components import ComponentFields
135from .crates.components import ComponentPrivacy
136
137# Entity enums
138from .crates.entity import GatingScope
139from .crates.entity import ObjectiveUIStyle
140from .crates.entity import ValueUIStyle
141
142# Fireteam enums.
143from .crates.fireteams import FireteamActivity
144from .crates.fireteams import FireteamDate
145from .crates.fireteams import FireteamLanguage
146from .crates.fireteams import FireteamPlatform
147
148# Records enums
149from .crates.records import RecordState
150from .metadata import __about__
151from .metadata import __author__
152from .metadata import __docs__
153from .metadata import __email__
154from .metadata import __license__
155from .metadata import __url__
156from .metadata import __version__
157
158__all__ = [mod for mod in dir() if not mod.startswith("_")]  # type: ignore
@attrs.define(auto_exc=True)
class AiobungieError(builtins.RuntimeError):
70@attrs.define(auto_exc=True)
71class AiobungieError(RuntimeError):
72    """Base class that all other exceptions inherit from."""

Base class that all other exceptions inherit from.

AiobungieError()
2def __init__(self, ):
3    BaseException.__init__(self, )

Method generated by attrs for class AiobungieError.

Inherited Members
builtins.BaseException
with_traceback
args
@typing.final
class AmmoType(builtins.int, aiobungie.Enum):
635@typing.final
636class AmmoType(int, Enum):
637    """AN enum for Detyiny 2 ammo types."""
638
639    NONE = 0
640    PRIMARY = 1
641    SPECIAL = 2
642    HEAVY = 3

AN enum for Detyiny 2 ammo types.

NONE = <AmmoType.NONE: 0>
PRIMARY = <AmmoType.PRIMARY: 1>
SPECIAL = <AmmoType.SPECIAL: 2>
HEAVY = <AmmoType.HEAVY: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class BadRequest(aiobungie.HTTPError):
163@attrs.define(auto_exc=True)
164class BadRequest(HTTPError):
165    """An exception raised when requesting a resource with the provided data is wrong."""
166
167    url: typedefs.StrOrURL | None
168    """The URL/endpoint caused this error."""
169
170    body: typing.Any
171    """The response body."""
172
173    headers: multidict.CIMultiDictProxy[str]
174    """The response headers."""
175
176    http_status: http.HTTPStatus = attrs.field(
177        default=http.HTTPStatus.BAD_REQUEST, init=False
178    )

An exception raised when requesting a resource with the provided data is wrong.

BadRequest( message: str, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str])
2def __init__(self, message, url, body, headers):
3    self.message = message
4    self.url = url
5    self.body = body
6    self.headers = headers
7    self.http_status = attr_dict['http_status'].default
8    BaseException.__init__(self, self.message,self.url,self.body,self.headers)

Method generated by attrs for class BadRequest.

url: Union[str, yarl.URL, NoneType]

The URL/endpoint caused this error.

body: Any

The response body.

headers: multidict._multidict.CIMultiDictProxy[str]

The response headers.

http_status: http.HTTPStatus

The response status.

Inherited Members
HTTPError
message
builtins.BaseException
with_traceback
args
@typing.final
class ClanMemberType(builtins.int, aiobungie.Enum):
690@typing.final
691class ClanMemberType(int, Enum):
692    """An enum for bungie clan member types."""
693
694    NONE = 0
695    BEGINNER = 1
696    MEMBER = 2
697    ADMIN = 3
698    ACTING_FOUNDER = 4
699    FOUNDER = 5

An enum for bungie clan member types.

NONE = <ClanMemberType.NONE: 0>
BEGINNER = <ClanMemberType.BEGINNER: 1>
MEMBER = <ClanMemberType.MEMBER: 2>
ADMIN = <ClanMemberType.ADMIN: 3>
ACTING_FOUNDER = <ClanMemberType.ACTING_FOUNDER: 4>
FOUNDER = <ClanMemberType.FOUNDER: 5>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Class(builtins.int, aiobungie.Enum):
466@typing.final
467class Class(int, Enum):
468    """An Enum for Destiny character classes."""
469
470    TITAN = 0
471    HUNTER = 1
472    WARLOCK = 2
473    UNKNOWN = 3

An Enum for Destiny character classes.

TITAN = <Class.TITAN: 0>
HUNTER = <Class.HUNTER: 1>
WARLOCK = <Class.WARLOCK: 2>
UNKNOWN = <Class.UNKNOWN: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class Client(aiobungie.traits.ClientApp):
  58class Client(traits.ClientApp):
  59    """Standard Bungie API client application.
  60
  61    This client deserialize the REST JSON responses using `aiobungie.internal.factory.Factory`
  62    and returns `aiobungie.crates` Python object implementations of the responses.
  63
  64    A `aiobungie.RESTClient` REST client can also be used alone for low-level concepts.
  65
  66    Example
  67    -------
  68    ```py
  69    import aiobungie
  70
  71    client = aiobungie.Client('...')
  72
  73    async def main():
  74        async with client.rest:
  75            user = await client.fetch_current_user_memberships('...')
  76            print(user)
  77    ```
  78
  79    Parameters
  80    -----------
  81    token: `str`
  82        Your Bungie's API key or Token from the developer's portal.
  83
  84    Other Parameters
  85    ----------------
  86    max_retries : `int`
  87        The max retries number to retry if the request hit a `5xx` status code.
  88    client_secret : `str | None`
  89        An optional application client secret,
  90        This is only needed if you're fetching OAuth2 tokens with this client.
  91    client_id : `int | None`
  92        An optional application client id,
  93        This is only needed if you're fetching OAuth2 tokens with this client.
  94    debug: `"TRACE" | bool | int`
  95        The level of logging to enable.
  96    """
  97
  98    __slots__ = ("_rest", "_factory")
  99
 100    def __init__(
 101        self,
 102        token: str,
 103        /,
 104        *,
 105        client_secret: str | None = None,
 106        client_id: int | None = None,
 107        max_retries: int = 4,
 108        debug: typing.Literal["TRACE"] | bool | int = False,
 109    ) -> None:
 110        self._rest = rest_.RESTClient(
 111            token,
 112            client_secret=client_secret,
 113            client_id=client_id,
 114            max_retries=max_retries,
 115            debug=debug,
 116        )
 117
 118        self._factory = factory_.Factory(self)
 119
 120    @property
 121    def factory(self) -> factory_.Factory:
 122        return self._factory
 123
 124    @property
 125    def rest(self) -> interfaces.RESTInterface:
 126        return self._rest
 127
 128    @property
 129    def request(self) -> Client:
 130        return self
 131
 132    @property
 133    def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]:
 134        return self._rest.metadata
 135
 136    def run(self, fn: collections.Awaitable[typing.Any], debug: bool = False) -> None:
 137        loop = helpers.get_or_make_loop()
 138
 139        try:
 140            if not loop.is_running():
 141                loop.set_debug(debug)
 142                loop.run_until_complete(fn)
 143
 144        except Exception as exc:
 145            raise RuntimeError(f"Failed to run {fn!s}") from exc
 146
 147        except KeyboardInterrupt:
 148            _LOGGER.warning("Unexpected Keyboard interrupt. Exiting.")
 149
 150    # * User methods.
 151
 152    async def fetch_current_user_memberships(self, access_token: str, /) -> user.User:
 153        """Fetch and return a user object of the bungie net user associated with account.
 154
 155        .. warning::
 156            This method requires OAuth2 scope and a Bearer access token.
 157
 158        Parameters
 159        ----------
 160        access_token : `str`
 161            A valid Bearer access token for the authorization.
 162
 163        Returns
 164        -------
 165        `aiobungie.crates.user.User`
 166            A user object includes the Destiny memberships and Bungie.net user.
 167        """
 168        resp = await self.rest.fetch_current_user_memberships(access_token)
 169
 170        return self.factory.deserialize_user(resp)
 171
 172    async def fetch_bungie_user(self, id: int, /) -> user.BungieUser:
 173        """Fetch a Bungie user by their BungieNet id.
 174
 175        .. note::
 176            This returns a Bungie user membership only. Take a look at `Client.fetch_membership_from_id`
 177            for other memberships.
 178
 179        Parameters
 180        ----------
 181        id: `int`
 182            The user id.
 183
 184        Returns
 185        -------
 186        `aiobungie.crates.user.BungieUser`
 187            A Bungie user.
 188
 189        Raises
 190        ------
 191        `aiobungie.error.NotFound`
 192            The user was not found.
 193        """
 194        payload = await self.rest.fetch_bungie_user(id)
 195
 196        return self.factory.deserialize_bungie_user(payload)
 197
 198    async def search_users(
 199        self, name: str, /
 200    ) -> iterators.Iterator[user.SearchableDestinyUser]:
 201        """Search for players and return all players that matches the same name.
 202
 203        Parameters
 204        ----------
 205        name : `str`
 206            The user name.
 207
 208        Returns
 209        -------
 210        `aiobungie.Iterator[aiobungie.crates.DestinyMembership]`
 211            A sequence of destiny memberships.
 212        """
 213        payload = await self.rest.search_users(name)
 214
 215        return iterators.Iterator(
 216            [
 217                self.factory.deserialize_searched_user(user)
 218                for user in payload["searchResults"]
 219            ]
 220        )
 221
 222    async def fetch_user_themes(self) -> collections.Sequence[user.UserThemes]:
 223        """Fetch all available user themes.
 224
 225        Returns
 226        -------
 227        `collections.Sequence[aiobungie.crates.user.UserThemes]`
 228            A sequence of user themes.
 229        """
 230        data = await self.rest.fetch_user_themes()
 231
 232        return self.factory.deserialize_user_themes(data)
 233
 234    async def fetch_hard_types(
 235        self,
 236        credential: int,
 237        type: enums.CredentialType | int = enums.CredentialType.STEAMID,
 238        /,
 239    ) -> user.HardLinkedMembership:
 240        """Gets any hard linked membership given a credential.
 241        Only works for credentials that are public just `aiobungie.CredentialType.STEAMID` right now.
 242        Cross Save aware.
 243
 244        Parameters
 245        ----------
 246        credential: `int`
 247            A valid SteamID64
 248        type: `aiobungie.CredentialType`
 249            The credential type. This must not be changed
 250            Since its only credential that works "currently"
 251
 252        Returns
 253        -------
 254        `aiobungie.crates.user.HardLinkedMembership`
 255            Information about the hard linked data.
 256        """
 257
 258        payload = await self.rest.fetch_hardlinked_credentials(credential, type)
 259
 260        return user.HardLinkedMembership(
 261            id=int(payload["membershipId"]),
 262            type=enums.MembershipType(payload["membershipType"]),
 263            cross_save_type=enums.MembershipType(payload["CrossSaveOverriddenType"]),
 264        )
 265
 266    async def fetch_membership_from_id(
 267        self,
 268        id: int,
 269        /,
 270        type: enums.MembershipType | int = enums.MembershipType.NONE,
 271    ) -> user.User:
 272        """Fetch Bungie user's memberships from their id.
 273
 274        Notes
 275        -----
 276        * This returns both BungieNet membership and a sequence of the player's DestinyMemberships
 277        Which includes Stadia, Xbox, Steam and PSN memberships if the player has them,
 278        see `aiobungie.crates.user.DestinyMembership` for more details.
 279        * If you only want the bungie user. Consider using `Client.fetch_user` method.
 280
 281        Parameters
 282        ----------
 283        id : `int`
 284            The user's id.
 285        type : `aiobungie.MembershipType`
 286            The user's membership type.
 287
 288        Returns
 289        -------
 290        `aiobungie.crates.User`
 291            A Bungie user with their membership types.
 292
 293        Raises
 294        ------
 295        aiobungie.NotFound
 296            The requested user was not found.
 297        """
 298        payload = await self.rest.fetch_membership_from_id(id, type)
 299
 300        return self.factory.deserialize_user(payload)
 301
 302    async def fetch_user_credentials(
 303        self, access_token: str, membership_id: int, /
 304    ) -> collections.Sequence[user.UserCredentials]:
 305        """Fetch an array of credential types attached to the requested account.
 306
 307        .. note::
 308            This method require OAuth2 Bearer access token.
 309
 310        Parameters
 311        ----------
 312        access_token : `str`
 313            The bearer access token associated with the bungie account.
 314        membership_id : `int`
 315            The id of the membership to return.
 316
 317        Returns
 318        -------
 319        `collections.Sequence[aiobungie.crates.UserCredentials]`
 320            A sequence of the attached user credentials.
 321
 322        Raises
 323        ------
 324        `aiobungie.Unauthorized`
 325            The access token was wrong or no access token passed.
 326        """
 327        resp = await self.rest.fetch_user_credentials(access_token, membership_id)
 328
 329        return self.factory.deserialize_user_credentials(resp)
 330
 331    # * Destiny 2.
 332
 333    async def fetch_profile(
 334        self,
 335        member_id: int,
 336        type: enums.MembershipType | int,
 337        components: collections.Sequence[enums.ComponentType],
 338        auth: str | None = None,
 339    ) -> components.Component:
 340        """
 341        Fetch a bungie profile passing components to the request.
 342
 343        Parameters
 344        ----------
 345        member_id: `int`
 346            The member's id.
 347        type: `aiobungie.MembershipType`
 348            A valid membership type.
 349        components : `collections.Sequence[aiobungie.ComponentType]`
 350            List of profile components to collect and return.
 351
 352        Other Parameters
 353        ----------------
 354        auth : `str | None`
 355            A Bearer access_token to make the request with.
 356            This is optional and limited to components that only requires an Authorization token.
 357
 358        Returns
 359        --------
 360        `aiobungie.crates.Component`
 361            A Destiny 2 player profile with its components.
 362            Only passed components will be available if they exists. Otherwise they will be `None`
 363
 364        Raises
 365        ------
 366        `aiobungie.MembershipTypeError`
 367            The provided membership type was invalid.
 368        """
 369        data = await self.rest.fetch_profile(member_id, type, components, auth)
 370        return self.factory.deserialize_components(data)
 371
 372    async def fetch_linked_profiles(
 373        self,
 374        member_id: int,
 375        member_type: enums.MembershipType | int,
 376        /,
 377        *,
 378        all: bool = False,
 379    ) -> profile.LinkedProfile:
 380        """Returns a summary information about all profiles linked to the requested member.
 381
 382        The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.
 383
 384        .. note::
 385            It will only return linked accounts whose linkages you are allowed to view.
 386
 387        Parameters
 388        ----------
 389        member_id : `int`
 390            The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID.
 391        member_type : `aiobungie.MembershipType`
 392            The type for the membership whose linked Destiny account you want to return.
 393
 394        Other Parameters
 395        ----------------
 396        all : `bool`
 397            If provided and set to `True`, All memberships regardless
 398            of whether they're obscured by overrides will be returned,
 399
 400            If provided and set to `False`, Only available memberships will be returned.
 401            The default for this is `False`.
 402
 403        Returns
 404        -------
 405        `aiobungie.crates.profile.LinkedProfile`
 406            A linked profile object.
 407        """
 408        resp = await self.rest.fetch_linked_profiles(member_id, member_type, all=all)
 409
 410        return self.factory.deserialize_linked_profiles(resp)
 411
 412    async def fetch_membership(
 413        self,
 414        name: str,
 415        code: int,
 416        /,
 417        type: enums.MembershipType | int = enums.MembershipType.ALL,
 418    ) -> collections.Sequence[user.DestinyMembership]:
 419        """Fetch a Destiny 2 player's memberships.
 420
 421        Parameters
 422        -----------
 423        name: `str`
 424            The unique Bungie player name.
 425        code : `int`
 426            The unique Bungie display name code.
 427        type: `aiobungie.internal.enums.MembershipType`
 428            The player's membership type, e,g. XBOX, STEAM, PSN
 429
 430        Returns
 431        --------
 432        `collections.Sequence[aiobungie.crates.DestinyMembership]`
 433            A sequence of the found Destiny 2 player memberships.
 434            An empty sequence will be returned if no one found.
 435
 436        Raises
 437        ------
 438        `aiobungie.MembershipTypeError`
 439            The provided membership type was invalid.
 440        """
 441        resp = await self.rest.fetch_membership(name, code, type)
 442
 443        return self.factory.deserialize_destiny_memberships(resp)
 444
 445    async def fetch_character(
 446        self,
 447        member_id: int,
 448        membership_type: enums.MembershipType | int,
 449        character_id: int,
 450        components: collections.Sequence[enums.ComponentType],
 451        auth: str | None = None,
 452    ) -> components.CharacterComponent:
 453        """Fetch a Destiny 2 character.
 454
 455        Parameters
 456        ----------
 457        member_id: `int`
 458            A valid bungie member id.
 459        character_id: `int`
 460            The Destiny character id to retrieve.
 461        membership_type: `aiobungie.internal.enums.MembershipType`
 462            The member's membership type.
 463        components: `collections.Sequence[aiobungie.ComponentType]`
 464            Multiple arguments of character components to collect and return.
 465
 466        Other Parameters
 467        ----------------
 468        auth : `str | None`
 469            A Bearer access_token to make the request with.
 470            This is optional and limited to components that only requires an Authorization token.
 471
 472        Returns
 473        -------
 474        `aiobungie.crates.CharacterComponent`
 475            A Bungie character component.
 476
 477        `aiobungie.MembershipTypeError`
 478            The provided membership type was invalid.
 479        """
 480        resp = await self.rest.fetch_character(
 481            member_id, membership_type, character_id, components, auth
 482        )
 483
 484        return self.factory.deserialize_character_component(resp)
 485
 486    async def fetch_unique_weapon_history(
 487        self,
 488        membership_id: int,
 489        character_id: int,
 490        membership_type: enums.MembershipType | int,
 491    ) -> collections.Sequence[activity.ExtendedWeaponValues]:
 492        """Fetch details about unique weapon usage for a character. Includes all exotics.
 493
 494        Parameters
 495        ----------
 496        membership_id : `int`
 497            The Destiny user membership id.
 498        character_id : `int`
 499            The character id to retrieve.
 500        membership_type : `aiobungie.aiobungie.MembershipType | int`
 501            The Destiny user's membership type.
 502
 503        Returns
 504        -------
 505        `collections.Sequence[aiobungie.crates.ExtendedWeaponValues]`
 506            A sequence of the weapon's extended values.
 507        """
 508        resp = await self._rest.fetch_unique_weapon_history(
 509            membership_id, character_id, membership_type
 510        )
 511
 512        return tuple(
 513            self._factory.deserialize_extended_weapon_values(weapon)
 514            for weapon in resp["weapons"]
 515        )
 516
 517    # * Destiny 2 Activities.
 518
 519    async def fetch_activities(
 520        self,
 521        member_id: int,
 522        character_id: int,
 523        mode: enums.GameMode | int,
 524        *,
 525        membership_type: enums.MembershipType | int = enums.MembershipType.ALL,
 526        page: int = 0,
 527        limit: int = 250,
 528    ) -> iterators.Iterator[activity.Activity]:
 529        """Fetch a Destiny 2 activity for the specified character id.
 530
 531        Parameters
 532        ----------
 533        member_id: `int`
 534            The user id that starts with `4611`.
 535        character_id: `int`
 536            The id of the character to retrieve the activities for.
 537        mode: `aiobungie.aiobungie.internal.enums.GameMode | int`
 538            This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
 539
 540        Other Parameters
 541        ----------------
 542        membership_type: `aiobungie.internal.enums.MembershipType`
 543            The Member ship type, if nothing was passed than it will return all.
 544        page: int
 545            The page number. Default is `0`
 546        limit: int
 547            Limit the returned result. Default is `250`.
 548
 549        Returns
 550        -------
 551        `aiobungie.Iterator[aiobungie.crates.Activity]`
 552            An iterator of the player's activities.
 553
 554        Raises
 555        ------
 556        `aiobungie.MembershipTypeError`
 557            The provided membership type was invalid.
 558        """
 559        resp = await self.rest.fetch_activities(
 560            member_id,
 561            character_id,
 562            mode,
 563            membership_type=membership_type,
 564            page=page,
 565            limit=limit,
 566        )
 567
 568        return self.factory.deserialize_activities(resp)
 569
 570    async def fetch_post_activity(self, instance_id: int, /) -> activity.PostActivity:
 571        """Fetch a post activity details.
 572
 573        Parameters
 574        ----------
 575        instance_id: `int`
 576            The activity instance id.
 577
 578        Returns
 579        -------
 580        `aiobungie.crates.PostActivity`
 581           A post activity object.
 582        """
 583        resp = await self.rest.fetch_post_activity(instance_id)
 584
 585        return self.factory.deserialize_post_activity(resp)
 586
 587    async def fetch_aggregated_activity_stats(
 588        self,
 589        character_id: int,
 590        membership_id: int,
 591        membership_type: enums.MembershipType | int,
 592    ) -> iterators.Iterator[activity.AggregatedActivity]:
 593        """Fetch aggregated activity stats for a character.
 594
 595        Parameters
 596        ----------
 597        character_id: `int`
 598            The id of the character to retrieve the activities for.
 599        membership_id: `int`
 600            The id of the user that started with `4611`.
 601        membership_type: `aiobungie.internal.enums.MembershipType`
 602            The Member ship type.
 603
 604        Returns
 605        -------
 606        `aiobungie.Iterator[aiobungie.crates.AggregatedActivity]`
 607            An iterator of the player's activities.
 608
 609        Raises
 610        ------
 611        `aiobungie.MembershipTypeError`
 612            The provided membership type was invalid.
 613        """
 614        resp = await self.rest.fetch_aggregated_activity_stats(
 615            character_id, membership_id, membership_type
 616        )
 617
 618        return self.factory.deserialize_aggregated_activities(resp)
 619
 620    # * Destiny 2 Clans or GroupsV2.
 621
 622    async def fetch_clan_from_id(
 623        self,
 624        id: int,
 625        /,
 626        access_token: str | None = None,
 627    ) -> clans.Clan:
 628        """Fetch a Bungie Clan by its id.
 629
 630        Parameters
 631        -----------
 632        id: `int`
 633            The clan id.
 634
 635        Returns
 636        --------
 637        `aiobungie.crates.Clan`
 638            An Bungie clan.
 639
 640        Raises
 641        ------
 642        `aiobungie.NotFound`
 643            The clan was not found.
 644        """
 645        resp = await self.rest.fetch_clan_from_id(id, access_token)
 646
 647        return self.factory.deserialize_clan(resp)
 648
 649    async def fetch_clan(
 650        self,
 651        name: str,
 652        /,
 653        access_token: str | None = None,
 654        *,
 655        type: enums.GroupType | int = enums.GroupType.CLAN,
 656    ) -> clans.Clan:
 657        """Fetch a Clan by its name.
 658        This method will return the first clan found with given name.
 659
 660        Parameters
 661        ----------
 662        name: `str`
 663            The clan name
 664
 665        Other Parameters
 666        ----------------
 667        access_token : `str | None`
 668            An optional access token to make the request with.
 669
 670            If the token was bound to a member of the clan,
 671            This field `aiobungie.crates.Clan.current_user_membership` will be available
 672            and will return the membership of the user who made this request.
 673        type : `aiobungie.GroupType`
 674            The group type, Default is aiobungie.GroupType.CLAN.
 675
 676        Returns
 677        -------
 678        `aiobungie.crates.Clan`
 679            A Bungie clan.
 680
 681        Raises
 682        ------
 683        `aiobungie.NotFound`
 684            The clan was not found.
 685        """
 686        resp = await self.rest.fetch_clan(name, access_token, type=type)
 687
 688        return self.factory.deserialize_clan(resp)
 689
 690    async def fetch_clan_conversations(
 691        self, clan_id: int, /
 692    ) -> collections.Sequence[clans.ClanConversation]:
 693        """Fetch the conversations/chat channels of the given clan id.
 694
 695        Parameters
 696        ----------
 697        clan_id : `int`
 698            The clan id.
 699
 700        Returns
 701        `collections.Sequence[aiobungie.crates.ClanConversation]`
 702            A sequence of the clan chat channels.
 703        """
 704        resp = await self.rest.fetch_clan_conversations(clan_id)
 705
 706        return self.factory.deserialize_clan_conversations(resp)
 707
 708    async def fetch_clan_admins(
 709        self, clan_id: int, /
 710    ) -> iterators.Iterator[clans.ClanMember]:
 711        """Fetch the clan founder and admins.
 712
 713        Parameters
 714        ----------
 715        clan_id : `int`
 716            The clan id.
 717
 718        Returns
 719        -------
 720        `aiobungie.Iterator[aiobungie.crates.ClanMember]`
 721            An iterator over the found clan admins and founder.
 722
 723        Raises
 724        ------
 725        `aiobungie.NotFound`
 726            The requested clan was not found.
 727        """
 728        resp = await self.rest.fetch_clan_admins(clan_id)
 729
 730        return self.factory.deserialize_clan_members(resp)
 731
 732    async def fetch_groups_for_member(
 733        self,
 734        member_id: int,
 735        member_type: enums.MembershipType | int,
 736        /,
 737        *,
 738        filter: int = 0,
 739        group_type: enums.GroupType = enums.GroupType.CLAN,
 740    ) -> collections.Sequence[clans.GroupMember]:
 741        """Fetch information about the groups that a given member has joined.
 742
 743        Parameters
 744        ----------
 745        member_id : `int`
 746            The member's id
 747        member_type : `aiobungie.MembershipType`
 748            The member's membership type.
 749
 750        Other Parameters
 751        ----------------
 752        filter : `int`
 753            Filter apply to list of joined groups. This Default to `0`
 754        group_type : `aiobungie.GroupType`
 755            The group's type.
 756            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
 757
 758        Returns
 759        -------
 760        `collections.Sequence[aiobungie.crates.GroupMember]`
 761            A sequence of joined groups for the fetched member.
 762        """
 763        resp = await self.rest.fetch_groups_for_member(
 764            member_id, member_type, filter=filter, group_type=group_type
 765        )
 766
 767        return tuple(
 768            self.factory.deserialize_group_member(group) for group in resp["results"]
 769        )
 770
 771    async def fetch_potential_groups_for_member(
 772        self,
 773        member_id: int,
 774        member_type: enums.MembershipType | int,
 775        /,
 776        *,
 777        filter: int = 0,
 778        group_type: enums.GroupType | int = enums.GroupType.CLAN,
 779    ) -> collections.Sequence[clans.GroupMember]:
 780        """Fetch the potential groups for a clan member.
 781
 782        Parameters
 783        ----------
 784        member_id : `int`
 785            The member's id
 786        member_type : `aiobungie.aiobungie.MembershipType | int`
 787            The member's membership type.
 788
 789        Other Parameters
 790        ----------------
 791        filter : `int`
 792            Filter apply to list of joined groups. This Default to `0`
 793        group_type : `aiobungie.aiobungie.GroupType | int`
 794            The group's type.
 795            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
 796
 797        Returns
 798        -------
 799        `collections.Sequence[aiobungie.crates.GroupMember]`
 800            A sequence of joined potential groups for the fetched member.
 801        """
 802        resp = await self.rest.fetch_potential_groups_for_member(
 803            member_id, member_type, filter=filter, group_type=group_type
 804        )
 805
 806        return tuple(
 807            self.factory.deserialize_group_member(group) for group in resp["results"]
 808        )
 809
 810    async def fetch_clan_members(
 811        self,
 812        clan_id: int,
 813        /,
 814        *,
 815        name: str | None = None,
 816        type: enums.MembershipType | int = enums.MembershipType.NONE,
 817    ) -> iterators.Iterator[clans.ClanMember]:
 818        """Fetch Bungie clan members.
 819
 820        Parameters
 821        ----------
 822        clan_id : `int`
 823            The clans id
 824
 825        Other Parameters
 826        ----------------
 827        name : `str | None`
 828            If provided, Only players matching this name will be returned.
 829        type : `aiobungie.MembershipType`
 830            An optional clan member's membership type.
 831            This parameter is used to filter the returned results
 832            by the provided membership, For an example XBox memberships only,
 833            Otherwise will return all memberships.
 834
 835        Returns
 836        -------
 837        `aiobungie.Iterator[aiobungie.crates.ClanMember]`
 838            An iterator over the bungie clan members.
 839
 840        Raises
 841        ------
 842        `aiobungie.NotFound`
 843            The clan was not found.
 844        """
 845        resp = await self.rest.fetch_clan_members(clan_id, type=type, name=name)
 846
 847        return self.factory.deserialize_clan_members(resp)
 848
 849    async def fetch_clan_banners(self) -> collections.Sequence[clans.ClanBanner]:
 850        """Fetch the clan banners.
 851
 852        Returns
 853        -------
 854        `collections.Sequence[aiobungie.crates.ClanBanner]`
 855            A sequence of the clan banners.
 856        """
 857        resp = await self.rest.fetch_clan_banners()
 858
 859        return self.factory.deserialize_clan_banners(resp)
 860
 861    # This method is required to be here since it deserialize the clan.
 862    async def kick_clan_member(
 863        self,
 864        access_token: str,
 865        /,
 866        group_id: int,
 867        membership_id: int,
 868        membership_type: enums.MembershipType | int,
 869    ) -> clans.Clan:
 870        """Kick a member from the clan.
 871
 872        .. note::
 873            This request requires OAuth2: oauth2: `AdminGroups` scope.
 874
 875        Parameters
 876        ----------
 877        access_token : `str`
 878            The bearer access token associated with the bungie account.
 879        group_id: `int`
 880            The group id.
 881        membership_id : `int`
 882            The member id to kick.
 883        membership_type : `aiobungie.aiobungie.MembershipType | int`
 884            The member's membership type.
 885
 886        Returns
 887        -------
 888        `aiobungie.crates.clan.Clan`
 889            The clan that the member was kicked from.
 890        """
 891        resp = await self.rest.kick_clan_member(
 892            access_token,
 893            group_id=group_id,
 894            membership_id=membership_id,
 895            membership_type=membership_type,
 896        )
 897
 898        return self.factory.deserialize_clan(resp)
 899
 900    async def fetch_clan_weekly_rewards(self, clan_id: int) -> milestones.Milestone:
 901        """Fetch a Bungie clan's weekly reward state.
 902
 903        Parameters
 904        ----------
 905        clan_id : `int`
 906            The clan's id.
 907
 908        Returns
 909        -------
 910        `aiobungie.crates.Milestone`
 911            A runtime status of the clan's milestone data.
 912        """
 913
 914        resp = await self.rest.fetch_clan_weekly_rewards(clan_id)
 915
 916        return self.factory.deserialize_milestone(resp)
 917
 918    # * Destiny 2 Entities aka Definitions.
 919
 920    async def fetch_inventory_item(self, hash: int, /) -> entity.InventoryEntity:
 921        """Fetch a static inventory item entity given a its hash.
 922
 923        Parameters
 924        ----------
 925        hash: `int`
 926            Inventory item's hash.
 927
 928        Returns
 929        -------
 930        `aiobungie.crates.InventoryEntity`
 931            A bungie inventory item.
 932        """
 933        resp = await self.rest.fetch_inventory_item(hash)
 934
 935        return self.factory.deserialize_inventory_entity(resp)
 936
 937    async def fetch_objective_entity(self, hash: int, /) -> entity.ObjectiveEntity:
 938        """Fetch a Destiny objective entity given a its hash.
 939
 940        Parameters
 941        ----------
 942        hash: `int`
 943            objective's hash.
 944
 945        Returns
 946        -------
 947        `aiobungie.crates.ObjectiveEntity`
 948            An objective entity item.
 949        """
 950        resp = await self.rest.fetch_objective_entity(hash)
 951
 952        return self.factory.deserialize_objective_entity(resp)
 953
 954    async def search_entities(
 955        self, name: str, entity_type: str, *, page: int = 0
 956    ) -> iterators.Iterator[entity.SearchableEntity]:
 957        """Search for Destiny2 entities given a name and its type.
 958
 959        Parameters
 960        ----------
 961        name : `str`
 962            The name of the entity, i.e., Thunderlord, One thousand voices.
 963        entity_type : `str`
 964            The type of the entity, AKA Definition,
 965            For an example `DestinyInventoryItemDefinition` for emblems, weapons, and other inventory items.
 966
 967        Other Parameters
 968        ----------------
 969        page : `int`
 970            An optional page to return. Default to 0.
 971
 972        Returns
 973        -------
 974        `aiobungie.Iterator[aiobungie.crates.SearchableEntity]`
 975            An iterator over the found results matching the provided name.
 976        """
 977        resp = await self.rest.search_entities(name, entity_type, page=page)
 978
 979        return self.factory.deserialize_inventory_results(resp)
 980
 981    # Fireteams
 982
 983    async def fetch_fireteams(
 984        self,
 985        activity_type: fireteams.FireteamActivity | int,
 986        *,
 987        platform: fireteams.FireteamPlatform | int = fireteams.FireteamPlatform.ANY,
 988        language: fireteams.FireteamLanguage | str = fireteams.FireteamLanguage.ALL,
 989        date_range: int = 0,
 990        page: int = 0,
 991        slots_filter: int = 0,
 992    ) -> collections.Sequence[fireteams.Fireteam]:
 993        """Fetch public Bungie fireteams with open slots.
 994
 995        Parameters
 996        ----------
 997        activity_type : `aiobungie.aiobungie.crates.FireteamActivity | int`
 998            The fireteam activity type.
 999
1000        Other Parameters
1001        ----------------
1002        platform : `aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int`
1003            If this is provided. Then the results will be filtered with the given platform.
1004            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1005        language : `aiobungie.crates.fireteams.FireteamLanguage | str`
1006            A locale language to filter the used language in that fireteam.
1007            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1008        date_range : `int`
1009            An integer to filter the date range of the returned fireteams. Defaults to `aiobungie.FireteamDate.ALL`.
1010        page : `int`
1011            The page number. By default its `0` which returns all available activities.
1012        slots_filter : `int`
1013            Filter the returned fireteams based on available slots. Default is `0`
1014
1015        Returns
1016        -------
1017        `collections.Sequence[fireteams.Fireteam]`
1018            A sequence of `aiobungie.crates.Fireteam`.
1019        """
1020
1021        resp = await self.rest.fetch_fireteams(
1022            activity_type,
1023            platform=platform,
1024            language=language,
1025            date_range=date_range,
1026            page=page,
1027            slots_filter=slots_filter,
1028        )
1029
1030        return self.factory.deserialize_fireteams(resp)
1031
1032    async def fetch_available_clan_fireteams(
1033        self,
1034        access_token: str,
1035        group_id: int,
1036        activity_type: fireteams.FireteamActivity | int,
1037        *,
1038        platform: fireteams.FireteamPlatform | int,
1039        language: fireteams.FireteamLanguage | str,
1040        date_range: int = 0,
1041        page: int = 0,
1042        public_only: bool = False,
1043        slots_filter: int = 0,
1044    ) -> collections.Sequence[fireteams.Fireteam]:
1045        """Fetch a clan's fireteams with open slots.
1046
1047        .. note::
1048            This method requires OAuth2: ReadGroups scope.
1049
1050        Parameters
1051        ----------
1052        access_token : `str`
1053            The bearer access token associated with the bungie account.
1054        group_id : `int`
1055            The group/clan id of the fireteam.
1056        activity_type : `aiobungie.aiobungie.crates.FireteamActivity | int`
1057            The fireteam activity type.
1058
1059        Other Parameters
1060        ----------------
1061        platform : `aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int`
1062            If this is provided. Then the results will be filtered with the given platform.
1063            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1064        language : `aiobungie.crates.fireteams.FireteamLanguage | str`
1065            A locale language to filter the used language in that fireteam.
1066            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1067        date_range : `int`
1068            An integer to filter the date range of the returned fireteams. Defaults to `0`.
1069        page : `int`
1070            The page number. By default its `0` which returns all available activities.
1071        public_only: `bool`
1072            If set to True, Then only public fireteams will be returned.
1073        slots_filter : `int`
1074            Filter the returned fireteams based on available slots. Default is `0`
1075
1076        Returns
1077        -------
1078        `collections.Sequence[aiobungie.crates.Fireteam]`
1079            A sequence of  fireteams found in the clan.
1080            `None` will be returned if nothing was found.
1081        """
1082        resp = await self.rest.fetch_available_clan_fireteams(
1083            access_token,
1084            group_id,
1085            activity_type,
1086            platform=platform,
1087            language=language,
1088            date_range=date_range,
1089            page=page,
1090            public_only=public_only,
1091            slots_filter=slots_filter,
1092        )
1093
1094        return self.factory.deserialize_fireteams(resp)
1095
1096    async def fetch_clan_fireteam(
1097        self, access_token: str, fireteam_id: int, group_id: int
1098    ) -> fireteams.AvailableFireteam:
1099        """Fetch a specific clan fireteam.
1100
1101        .. note::
1102            This method requires OAuth2: ReadGroups scope.
1103
1104        Parameters
1105        ----------
1106        access_token : `str`
1107            The bearer access token associated with the bungie account.
1108        group_id : `int`
1109            The group/clan id to fetch the fireteam from.
1110        fireteam_id : `int`
1111            The fireteam id to fetch.
1112
1113        Returns
1114        -------
1115        `aiobungie.crates.AvailableFireteam`
1116            A sequence of available fireteams objects.
1117        """
1118        resp = await self.rest.fetch_clan_fireteam(access_token, fireteam_id, group_id)
1119
1120        return self.factory.deserialize_available_fireteam(resp)
1121
1122    async def fetch_my_clan_fireteams(
1123        self,
1124        access_token: str,
1125        group_id: int,
1126        *,
1127        include_closed: bool = True,
1128        platform: fireteams.FireteamPlatform | int,
1129        language: fireteams.FireteamLanguage | str,
1130        filtered: bool = True,
1131        page: int = 0,
1132    ) -> collections.Sequence[fireteams.AvailableFireteam]:
1133        """A method that's similar to `fetch_fireteams` but requires OAuth2.
1134
1135        .. note::
1136            This method requires OAuth2: ReadGroups scope.
1137
1138        Parameters
1139        ----------
1140        access_token : str
1141            The bearer access token associated with the bungie account.
1142        group_id : int
1143            The group/clan id to fetch.
1144
1145        Other Parameters
1146        ----------------
1147        include_closed : `bool`
1148            If provided and set to True, It will also return closed fireteams.
1149            If provided and set to False, It will only return public fireteams. Default is True.
1150        platform : aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int
1151            If this is provided. Then the results will be filtered with the given platform.
1152            Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
1153        language : `aiobungie.crates.fireteams.FireteamLanguage | str`
1154            A locale language to filter the used language in that fireteam.
1155            Defaults to aiobungie.crates.FireteamLanguage.ALL
1156        filtered : `bool`
1157            If set to True, it will filter by clan. Otherwise not. Default is True.
1158        page : `int`
1159            The page number. By default its 0 which returns all available activities.
1160
1161        Returns
1162        -------
1163        `collections.Sequence[aiobungie.crates.AvailableFireteam]`
1164            A sequence of available fireteams objects if exists. else `None` will be returned.
1165        """
1166        resp = await self.rest.fetch_my_clan_fireteams(
1167            access_token,
1168            group_id,
1169            include_closed=include_closed,
1170            platform=platform,
1171            language=language,
1172            filtered=filtered,
1173            page=page,
1174        )
1175
1176        return self.factory.deserialize_available_fireteams(resp)
1177
1178    # Friends and social.
1179
1180    async def fetch_friends(
1181        self, access_token: str, /
1182    ) -> collections.Sequence[friends.Friend]:
1183        """Fetch bungie friend list.
1184
1185        .. note::
1186            This requests OAuth2: ReadUserData scope.
1187
1188        Parameters
1189        -----------
1190        access_token : `str`
1191            The bearer access token associated with the bungie account.
1192
1193        Returns
1194        -------
1195        `collections.Sequence[aiobungie.crates.Friend]`
1196            A sequence of the friends associated with that access token.
1197        """
1198
1199        resp = await self.rest.fetch_friends(access_token)
1200
1201        return self.factory.deserialize_friends(resp)
1202
1203    async def fetch_friend_requests(
1204        self, access_token: str, /
1205    ) -> friends.FriendRequestView:
1206        """Fetch pending bungie friend requests queue.
1207
1208        .. note::
1209            This requests OAuth2: ReadUserData scope.
1210
1211        Parameters
1212        -----------
1213        access_token : `str`
1214            The bearer access token associated with the bungie account.
1215
1216        Returns
1217        -------
1218        `aiobungie.crates.FriendRequestView`
1219            A friend requests view of that associated access token.
1220        """
1221
1222        resp = await self.rest.fetch_friend_requests(access_token)
1223
1224        return self.factory.deserialize_friend_requests(resp)
1225
1226    # Applications and Developer portal.
1227
1228    async def fetch_application(self, appid: int, /) -> application.Application:
1229        """Fetch a Bungie application.
1230
1231        Parameters
1232        -----------
1233        appid: `int`
1234            The application id.
1235
1236        Returns
1237        --------
1238        `aiobungie.crates.Application`
1239            A Bungie application.
1240        """
1241        resp = await self.rest.fetch_application(appid)
1242
1243        return self.factory.deserialize_app(resp)
1244
1245    # Milestones
1246
1247    async def fetch_public_milestone_content(
1248        self, milestone_hash: int, /
1249    ) -> milestones.MilestoneContent:
1250        """Fetch the milestone content given its hash.
1251
1252        Parameters
1253        ----------
1254        milestone_hash : `int`
1255            The milestone hash.
1256
1257        Returns
1258        -------
1259        `aiobungie.crates.milestones.MilestoneContent`
1260            A milestone content object.
1261        """
1262        ...
1263        resp = await self.rest.fetch_public_milestone_content(milestone_hash)
1264        return self.factory.deserialize_public_milestone_content(resp)

Standard Bungie API client application.

This client deserialize the REST JSON responses using Factory and returns aiobungie.crates Python object implementations of the responses.

A aiobungie.RESTClient REST client can also be used alone for low-level concepts.

Example
import aiobungie

client = aiobungie.Client('...')

async def main():
    async with client.rest:
        user = await client.fetch_current_user_memberships('...')
        print(user)
Parameters
  • token (str): Your Bungie's API key or Token from the developer's portal.
Other Parameters
  • max_retries (int): The max retries number to retry if the request hit a 5xx status code.
  • client_secret (str | None): An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client.
  • client_id (int | None): An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client.
  • debug ("TRACE" | bool | int): The level of logging to enable.
Client( token: str, /, *, client_secret: str | None = None, client_id: int | None = None, max_retries: int = 4, debug: Union[Literal['TRACE'], bool, int] = False)
100    def __init__(
101        self,
102        token: str,
103        /,
104        *,
105        client_secret: str | None = None,
106        client_id: int | None = None,
107        max_retries: int = 4,
108        debug: typing.Literal["TRACE"] | bool | int = False,
109    ) -> None:
110        self._rest = rest_.RESTClient(
111            token,
112            client_secret=client_secret,
113            client_id=client_id,
114            max_retries=max_retries,
115            debug=debug,
116        )
117
118        self._factory = factory_.Factory(self)
factory: Factory
120    @property
121    def factory(self) -> factory_.Factory:
122        return self._factory

Returns the marshalling factory for the client.

rest: aiobungie.interfaces.rest.RESTInterface
124    @property
125    def rest(self) -> interfaces.RESTInterface:
126        return self._rest

Returns the REST client for the this client.

request: Client
128    @property
129    def request(self) -> Client:
130        return self

A readonly ClientApp instance used for external requests.

metadata: collections.abc.MutableMapping[typing.Any, typing.Any]
132    @property
133    def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]:
134        return self._rest.metadata

A mutable mapping storage for the user's needs.

def run( self, fn: collections.abc.Awaitable[typing.Any], debug: bool = False) -> None:
136    def run(self, fn: collections.Awaitable[typing.Any], debug: bool = False) -> None:
137        loop = helpers.get_or_make_loop()
138
139        try:
140            if not loop.is_running():
141                loop.set_debug(debug)
142                loop.run_until_complete(fn)
143
144        except Exception as exc:
145            raise RuntimeError(f"Failed to run {fn!s}") from exc
146
147        except KeyboardInterrupt:
148            _LOGGER.warning("Unexpected Keyboard interrupt. Exiting.")

Runs a coroutine function until its complete.

This is equivalent to asyncio.get_event_loop().run_until_complete(...)

Parameters
  • fn (collections.Awaitable[Any]): The async function to run.
  • debug (bool): Either to enable asyncio debug or not. Disabled by default.
Example
async def main() -> None:
    await fetch(...)

# Run the coroutine.
client.run(main())
async def fetch_current_user_memberships(self, access_token: str, /) -> aiobungie.crates.user.User:
152    async def fetch_current_user_memberships(self, access_token: str, /) -> user.User:
153        """Fetch and return a user object of the bungie net user associated with account.
154
155        .. warning::
156            This method requires OAuth2 scope and a Bearer access token.
157
158        Parameters
159        ----------
160        access_token : `str`
161            A valid Bearer access token for the authorization.
162
163        Returns
164        -------
165        `aiobungie.crates.user.User`
166            A user object includes the Destiny memberships and Bungie.net user.
167        """
168        resp = await self.rest.fetch_current_user_memberships(access_token)
169
170        return self.factory.deserialize_user(resp)

Fetch and return a user object of the bungie net user associated with account.

This method requires OAuth2 scope and a Bearer access token.

Parameters
  • access_token (str): A valid Bearer access token for the authorization.
Returns
  • aiobungie.crates.user.User: A user object includes the Destiny memberships and Bungie.net user.
async def fetch_bungie_user(self, id: int, /) -> aiobungie.crates.user.BungieUser:
172    async def fetch_bungie_user(self, id: int, /) -> user.BungieUser:
173        """Fetch a Bungie user by their BungieNet id.
174
175        .. note::
176            This returns a Bungie user membership only. Take a look at `Client.fetch_membership_from_id`
177            for other memberships.
178
179        Parameters
180        ----------
181        id: `int`
182            The user id.
183
184        Returns
185        -------
186        `aiobungie.crates.user.BungieUser`
187            A Bungie user.
188
189        Raises
190        ------
191        `aiobungie.error.NotFound`
192            The user was not found.
193        """
194        payload = await self.rest.fetch_bungie_user(id)
195
196        return self.factory.deserialize_bungie_user(payload)

Fetch a Bungie user by their BungieNet id.

This returns a Bungie user membership only. Take a look at Client.fetch_membership_from_id for other memberships.

Parameters
  • id (int): The user id.
Returns
  • aiobungie.crates.user.BungieUser: A Bungie user.
Raises
async def search_users( self, name: str, /) -> Iterator[aiobungie.crates.user.SearchableDestinyUser]:
198    async def search_users(
199        self, name: str, /
200    ) -> iterators.Iterator[user.SearchableDestinyUser]:
201        """Search for players and return all players that matches the same name.
202
203        Parameters
204        ----------
205        name : `str`
206            The user name.
207
208        Returns
209        -------
210        `aiobungie.Iterator[aiobungie.crates.DestinyMembership]`
211            A sequence of destiny memberships.
212        """
213        payload = await self.rest.search_users(name)
214
215        return iterators.Iterator(
216            [
217                self.factory.deserialize_searched_user(user)
218                for user in payload["searchResults"]
219            ]
220        )

Search for players and return all players that matches the same name.

Parameters
  • name (str): The user name.
Returns
async def fetch_user_themes(self) -> collections.abc.Sequence[aiobungie.crates.user.UserThemes]:
222    async def fetch_user_themes(self) -> collections.Sequence[user.UserThemes]:
223        """Fetch all available user themes.
224
225        Returns
226        -------
227        `collections.Sequence[aiobungie.crates.user.UserThemes]`
228            A sequence of user themes.
229        """
230        data = await self.rest.fetch_user_themes()
231
232        return self.factory.deserialize_user_themes(data)

Fetch all available user themes.

Returns
  • collections.Sequence[aiobungie.crates.user.UserThemes]: A sequence of user themes.
async def fetch_hard_types( self, credential: int, type: CredentialType | int = <CredentialType.STEAMID: 12>, /) -> aiobungie.crates.user.HardLinkedMembership:
234    async def fetch_hard_types(
235        self,
236        credential: int,
237        type: enums.CredentialType | int = enums.CredentialType.STEAMID,
238        /,
239    ) -> user.HardLinkedMembership:
240        """Gets any hard linked membership given a credential.
241        Only works for credentials that are public just `aiobungie.CredentialType.STEAMID` right now.
242        Cross Save aware.
243
244        Parameters
245        ----------
246        credential: `int`
247            A valid SteamID64
248        type: `aiobungie.CredentialType`
249            The credential type. This must not be changed
250            Since its only credential that works "currently"
251
252        Returns
253        -------
254        `aiobungie.crates.user.HardLinkedMembership`
255            Information about the hard linked data.
256        """
257
258        payload = await self.rest.fetch_hardlinked_credentials(credential, type)
259
260        return user.HardLinkedMembership(
261            id=int(payload["membershipId"]),
262            type=enums.MembershipType(payload["membershipType"]),
263            cross_save_type=enums.MembershipType(payload["CrossSaveOverriddenType"]),
264        )

Gets any hard linked membership given a credential. Only works for credentials that are public just aiobungie.CredentialType.STEAMID right now. Cross Save aware.

Parameters
  • credential (int): A valid SteamID64
  • type (aiobungie.CredentialType): The credential type. This must not be changed Since its only credential that works "currently"
Returns
  • aiobungie.crates.user.HardLinkedMembership: Information about the hard linked data.
async def fetch_membership_from_id( self, id: int, /, type: MembershipType | int = <MembershipType.NONE: 0>) -> aiobungie.crates.user.User:
266    async def fetch_membership_from_id(
267        self,
268        id: int,
269        /,
270        type: enums.MembershipType | int = enums.MembershipType.NONE,
271    ) -> user.User:
272        """Fetch Bungie user's memberships from their id.
273
274        Notes
275        -----
276        * This returns both BungieNet membership and a sequence of the player's DestinyMemberships
277        Which includes Stadia, Xbox, Steam and PSN memberships if the player has them,
278        see `aiobungie.crates.user.DestinyMembership` for more details.
279        * If you only want the bungie user. Consider using `Client.fetch_user` method.
280
281        Parameters
282        ----------
283        id : `int`
284            The user's id.
285        type : `aiobungie.MembershipType`
286            The user's membership type.
287
288        Returns
289        -------
290        `aiobungie.crates.User`
291            A Bungie user with their membership types.
292
293        Raises
294        ------
295        aiobungie.NotFound
296            The requested user was not found.
297        """
298        payload = await self.rest.fetch_membership_from_id(id, type)
299
300        return self.factory.deserialize_user(payload)

Fetch Bungie user's memberships from their id.

Notes
  • This returns both BungieNet membership and a sequence of the player's DestinyMemberships Which includes Stadia, Xbox, Steam and PSN memberships if the player has them, see aiobungie.crates.user.DestinyMembership for more details.
  • If you only want the bungie user. Consider using Client.fetch_user method.
Parameters
Returns
Raises
async def fetch_user_credentials( self, access_token: str, membership_id: int, /) -> collections.abc.Sequence[aiobungie.crates.user.UserCredentials]:
302    async def fetch_user_credentials(
303        self, access_token: str, membership_id: int, /
304    ) -> collections.Sequence[user.UserCredentials]:
305        """Fetch an array of credential types attached to the requested account.
306
307        .. note::
308            This method require OAuth2 Bearer access token.
309
310        Parameters
311        ----------
312        access_token : `str`
313            The bearer access token associated with the bungie account.
314        membership_id : `int`
315            The id of the membership to return.
316
317        Returns
318        -------
319        `collections.Sequence[aiobungie.crates.UserCredentials]`
320            A sequence of the attached user credentials.
321
322        Raises
323        ------
324        `aiobungie.Unauthorized`
325            The access token was wrong or no access token passed.
326        """
327        resp = await self.rest.fetch_user_credentials(access_token, membership_id)
328
329        return self.factory.deserialize_user_credentials(resp)

Fetch an array of credential types attached to the requested account.

This method require OAuth2 Bearer access token.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • membership_id (int): The id of the membership to return.
Returns
Raises
async def fetch_profile( self, member_id: int, type: MembershipType | int, components: collections.abc.Sequence[ComponentType], auth: str | None = None) -> aiobungie.crates.components.Component:
333    async def fetch_profile(
334        self,
335        member_id: int,
336        type: enums.MembershipType | int,
337        components: collections.Sequence[enums.ComponentType],
338        auth: str | None = None,
339    ) -> components.Component:
340        """
341        Fetch a bungie profile passing components to the request.
342
343        Parameters
344        ----------
345        member_id: `int`
346            The member's id.
347        type: `aiobungie.MembershipType`
348            A valid membership type.
349        components : `collections.Sequence[aiobungie.ComponentType]`
350            List of profile components to collect and return.
351
352        Other Parameters
353        ----------------
354        auth : `str | None`
355            A Bearer access_token to make the request with.
356            This is optional and limited to components that only requires an Authorization token.
357
358        Returns
359        --------
360        `aiobungie.crates.Component`
361            A Destiny 2 player profile with its components.
362            Only passed components will be available if they exists. Otherwise they will be `None`
363
364        Raises
365        ------
366        `aiobungie.MembershipTypeError`
367            The provided membership type was invalid.
368        """
369        data = await self.rest.fetch_profile(member_id, type, components, auth)
370        return self.factory.deserialize_components(data)

Fetch a bungie profile passing components to the request.

Parameters
Other Parameters
  • auth (str | None): A Bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
  • aiobungie.crates.Component: A Destiny 2 player profile with its components. Only passed components will be available if they exists. Otherwise they will be None
Raises
async def fetch_linked_profiles( self, member_id: int, member_type: MembershipType | int, /, *, all: bool = False) -> aiobungie.crates.profile.LinkedProfile:
372    async def fetch_linked_profiles(
373        self,
374        member_id: int,
375        member_type: enums.MembershipType | int,
376        /,
377        *,
378        all: bool = False,
379    ) -> profile.LinkedProfile:
380        """Returns a summary information about all profiles linked to the requested member.
381
382        The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.
383
384        .. note::
385            It will only return linked accounts whose linkages you are allowed to view.
386
387        Parameters
388        ----------
389        member_id : `int`
390            The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID.
391        member_type : `aiobungie.MembershipType`
392            The type for the membership whose linked Destiny account you want to return.
393
394        Other Parameters
395        ----------------
396        all : `bool`
397            If provided and set to `True`, All memberships regardless
398            of whether they're obscured by overrides will be returned,
399
400            If provided and set to `False`, Only available memberships will be returned.
401            The default for this is `False`.
402
403        Returns
404        -------
405        `aiobungie.crates.profile.LinkedProfile`
406            A linked profile object.
407        """
408        resp = await self.rest.fetch_linked_profiles(member_id, member_type, all=all)
409
410        return self.factory.deserialize_linked_profiles(resp)

Returns a summary information about all profiles linked to the requested member.

The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.

It will only return linked accounts whose linkages you are allowed to view.

Parameters
  • member_id (int): The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID.
  • member_type (aiobungie.MembershipType): The type for the membership whose linked Destiny account you want to return.
Other Parameters
  • all (bool): If provided and set to True, All memberships regardless of whether they're obscured by overrides will be returned,

    If provided and set to False, Only available memberships will be returned. The default for this is False.

Returns
  • aiobungie.crates.profile.LinkedProfile: A linked profile object.
async def fetch_membership( self, name: str, code: int, /, type: MembershipType | int = <MembershipType.ALL: -1>) -> collections.abc.Sequence[aiobungie.crates.user.DestinyMembership]:
412    async def fetch_membership(
413        self,
414        name: str,
415        code: int,
416        /,
417        type: enums.MembershipType | int = enums.MembershipType.ALL,
418    ) -> collections.Sequence[user.DestinyMembership]:
419        """Fetch a Destiny 2 player's memberships.
420
421        Parameters
422        -----------
423        name: `str`
424            The unique Bungie player name.
425        code : `int`
426            The unique Bungie display name code.
427        type: `aiobungie.internal.enums.MembershipType`
428            The player's membership type, e,g. XBOX, STEAM, PSN
429
430        Returns
431        --------
432        `collections.Sequence[aiobungie.crates.DestinyMembership]`
433            A sequence of the found Destiny 2 player memberships.
434            An empty sequence will be returned if no one found.
435
436        Raises
437        ------
438        `aiobungie.MembershipTypeError`
439            The provided membership type was invalid.
440        """
441        resp = await self.rest.fetch_membership(name, code, type)
442
443        return self.factory.deserialize_destiny_memberships(resp)

Fetch a Destiny 2 player's memberships.

Parameters
  • name (str): The unique Bungie player name.
  • code (int): The unique Bungie display name code.
  • type (MembershipType): The player's membership type, e,g. XBOX, STEAM, PSN
Returns
Raises
async def fetch_character( self, member_id: int, membership_type: MembershipType | int, character_id: int, components: collections.abc.Sequence[ComponentType], auth: str | None = None) -> aiobungie.crates.components.CharacterComponent:
445    async def fetch_character(
446        self,
447        member_id: int,
448        membership_type: enums.MembershipType | int,
449        character_id: int,
450        components: collections.Sequence[enums.ComponentType],
451        auth: str | None = None,
452    ) -> components.CharacterComponent:
453        """Fetch a Destiny 2 character.
454
455        Parameters
456        ----------
457        member_id: `int`
458            A valid bungie member id.
459        character_id: `int`
460            The Destiny character id to retrieve.
461        membership_type: `aiobungie.internal.enums.MembershipType`
462            The member's membership type.
463        components: `collections.Sequence[aiobungie.ComponentType]`
464            Multiple arguments of character components to collect and return.
465
466        Other Parameters
467        ----------------
468        auth : `str | None`
469            A Bearer access_token to make the request with.
470            This is optional and limited to components that only requires an Authorization token.
471
472        Returns
473        -------
474        `aiobungie.crates.CharacterComponent`
475            A Bungie character component.
476
477        `aiobungie.MembershipTypeError`
478            The provided membership type was invalid.
479        """
480        resp = await self.rest.fetch_character(
481            member_id, membership_type, character_id, components, auth
482        )
483
484        return self.factory.deserialize_character_component(resp)

Fetch a Destiny 2 character.

Parameters
  • member_id (int): A valid bungie member id.
  • character_id (int): The Destiny character id to retrieve.
  • membership_type (MembershipType): The member's membership type.
  • components (collections.Sequence[aiobungie.ComponentType]): Multiple arguments of character components to collect and return.
Other Parameters
  • auth (str | None): A Bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
async def fetch_unique_weapon_history( self, membership_id: int, character_id: int, membership_type: MembershipType | int) -> collections.abc.Sequence[aiobungie.crates.activity.ExtendedWeaponValues]:
486    async def fetch_unique_weapon_history(
487        self,
488        membership_id: int,
489        character_id: int,
490        membership_type: enums.MembershipType | int,
491    ) -> collections.Sequence[activity.ExtendedWeaponValues]:
492        """Fetch details about unique weapon usage for a character. Includes all exotics.
493
494        Parameters
495        ----------
496        membership_id : `int`
497            The Destiny user membership id.
498        character_id : `int`
499            The character id to retrieve.
500        membership_type : `aiobungie.aiobungie.MembershipType | int`
501            The Destiny user's membership type.
502
503        Returns
504        -------
505        `collections.Sequence[aiobungie.crates.ExtendedWeaponValues]`
506            A sequence of the weapon's extended values.
507        """
508        resp = await self._rest.fetch_unique_weapon_history(
509            membership_id, character_id, membership_type
510        )
511
512        return tuple(
513            self._factory.deserialize_extended_weapon_values(weapon)
514            for weapon in resp["weapons"]
515        )

Fetch details about unique weapon usage for a character. Includes all exotics.

Parameters
  • membership_id (int): The Destiny user membership id.
  • character_id (int): The character id to retrieve.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The Destiny user's membership type.
Returns
async def fetch_activities( self, member_id: int, character_id: int, mode: GameMode | int, *, membership_type: MembershipType | int = <MembershipType.ALL: -1>, page: int = 0, limit: int = 250) -> Iterator[aiobungie.crates.activity.Activity]:
519    async def fetch_activities(
520        self,
521        member_id: int,
522        character_id: int,
523        mode: enums.GameMode | int,
524        *,
525        membership_type: enums.MembershipType | int = enums.MembershipType.ALL,
526        page: int = 0,
527        limit: int = 250,
528    ) -> iterators.Iterator[activity.Activity]:
529        """Fetch a Destiny 2 activity for the specified character id.
530
531        Parameters
532        ----------
533        member_id: `int`
534            The user id that starts with `4611`.
535        character_id: `int`
536            The id of the character to retrieve the activities for.
537        mode: `aiobungie.aiobungie.internal.enums.GameMode | int`
538            This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
539
540        Other Parameters
541        ----------------
542        membership_type: `aiobungie.internal.enums.MembershipType`
543            The Member ship type, if nothing was passed than it will return all.
544        page: int
545            The page number. Default is `0`
546        limit: int
547            Limit the returned result. Default is `250`.
548
549        Returns
550        -------
551        `aiobungie.Iterator[aiobungie.crates.Activity]`
552            An iterator of the player's activities.
553
554        Raises
555        ------
556        `aiobungie.MembershipTypeError`
557            The provided membership type was invalid.
558        """
559        resp = await self.rest.fetch_activities(
560            member_id,
561            character_id,
562            mode,
563            membership_type=membership_type,
564            page=page,
565            limit=limit,
566        )
567
568        return self.factory.deserialize_activities(resp)

Fetch a Destiny 2 activity for the specified character id.

Parameters
  • member_id (int): The user id that starts with 4611.
  • character_id (int): The id of the character to retrieve the activities for.
  • mode (aiobungie.aiobungie.internal.enums.GameMode | int): This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
Other Parameters
  • membership_type (MembershipType): The Member ship type, if nothing was passed than it will return all.
  • page (int): The page number. Default is 0
  • limit (int): Limit the returned result. Default is 250.
Returns
Raises
async def fetch_post_activity(self, instance_id: int, /) -> aiobungie.crates.activity.PostActivity:
570    async def fetch_post_activity(self, instance_id: int, /) -> activity.PostActivity:
571        """Fetch a post activity details.
572
573        Parameters
574        ----------
575        instance_id: `int`
576            The activity instance id.
577
578        Returns
579        -------
580        `aiobungie.crates.PostActivity`
581           A post activity object.
582        """
583        resp = await self.rest.fetch_post_activity(instance_id)
584
585        return self.factory.deserialize_post_activity(resp)

Fetch a post activity details.

Parameters
  • instance_id (int): The activity instance id.
Returns
async def fetch_aggregated_activity_stats( self, character_id: int, membership_id: int, membership_type: MembershipType | int) -> Iterator[aiobungie.crates.activity.AggregatedActivity]:
587    async def fetch_aggregated_activity_stats(
588        self,
589        character_id: int,
590        membership_id: int,
591        membership_type: enums.MembershipType | int,
592    ) -> iterators.Iterator[activity.AggregatedActivity]:
593        """Fetch aggregated activity stats for a character.
594
595        Parameters
596        ----------
597        character_id: `int`
598            The id of the character to retrieve the activities for.
599        membership_id: `int`
600            The id of the user that started with `4611`.
601        membership_type: `aiobungie.internal.enums.MembershipType`
602            The Member ship type.
603
604        Returns
605        -------
606        `aiobungie.Iterator[aiobungie.crates.AggregatedActivity]`
607            An iterator of the player's activities.
608
609        Raises
610        ------
611        `aiobungie.MembershipTypeError`
612            The provided membership type was invalid.
613        """
614        resp = await self.rest.fetch_aggregated_activity_stats(
615            character_id, membership_id, membership_type
616        )
617
618        return self.factory.deserialize_aggregated_activities(resp)

Fetch aggregated activity stats for a character.

Parameters
  • character_id (int): The id of the character to retrieve the activities for.
  • membership_id (int): The id of the user that started with 4611.
  • membership_type (MembershipType): The Member ship type.
Returns
Raises
async def fetch_clan_from_id( self, id: int, /, access_token: str | None = None) -> aiobungie.crates.clans.Clan:
622    async def fetch_clan_from_id(
623        self,
624        id: int,
625        /,
626        access_token: str | None = None,
627    ) -> clans.Clan:
628        """Fetch a Bungie Clan by its id.
629
630        Parameters
631        -----------
632        id: `int`
633            The clan id.
634
635        Returns
636        --------
637        `aiobungie.crates.Clan`
638            An Bungie clan.
639
640        Raises
641        ------
642        `aiobungie.NotFound`
643            The clan was not found.
644        """
645        resp = await self.rest.fetch_clan_from_id(id, access_token)
646
647        return self.factory.deserialize_clan(resp)

Fetch a Bungie Clan by its id.

Parameters
  • id (int): The clan id.
Returns
Raises
async def fetch_clan( self, name: str, /, access_token: str | None = None, *, type: GroupType | int = <GroupType.CLAN: 1>) -> aiobungie.crates.clans.Clan:
649    async def fetch_clan(
650        self,
651        name: str,
652        /,
653        access_token: str | None = None,
654        *,
655        type: enums.GroupType | int = enums.GroupType.CLAN,
656    ) -> clans.Clan:
657        """Fetch a Clan by its name.
658        This method will return the first clan found with given name.
659
660        Parameters
661        ----------
662        name: `str`
663            The clan name
664
665        Other Parameters
666        ----------------
667        access_token : `str | None`
668            An optional access token to make the request with.
669
670            If the token was bound to a member of the clan,
671            This field `aiobungie.crates.Clan.current_user_membership` will be available
672            and will return the membership of the user who made this request.
673        type : `aiobungie.GroupType`
674            The group type, Default is aiobungie.GroupType.CLAN.
675
676        Returns
677        -------
678        `aiobungie.crates.Clan`
679            A Bungie clan.
680
681        Raises
682        ------
683        `aiobungie.NotFound`
684            The clan was not found.
685        """
686        resp = await self.rest.fetch_clan(name, access_token, type=type)
687
688        return self.factory.deserialize_clan(resp)

Fetch a Clan by its name. This method will return the first clan found with given name.

Parameters
  • name (str): The clan name
Other Parameters
Returns
Raises
async def fetch_clan_conversations( self, clan_id: int, /) -> collections.abc.Sequence[aiobungie.crates.clans.ClanConversation]:
690    async def fetch_clan_conversations(
691        self, clan_id: int, /
692    ) -> collections.Sequence[clans.ClanConversation]:
693        """Fetch the conversations/chat channels of the given clan id.
694
695        Parameters
696        ----------
697        clan_id : `int`
698            The clan id.
699
700        Returns
701        `collections.Sequence[aiobungie.crates.ClanConversation]`
702            A sequence of the clan chat channels.
703        """
704        resp = await self.rest.fetch_clan_conversations(clan_id)
705
706        return self.factory.deserialize_clan_conversations(resp)

Fetch the conversations/chat channels of the given clan id.

Parameters
async def fetch_clan_admins( self, clan_id: int, /) -> Iterator[aiobungie.crates.clans.ClanMember]:
708    async def fetch_clan_admins(
709        self, clan_id: int, /
710    ) -> iterators.Iterator[clans.ClanMember]:
711        """Fetch the clan founder and admins.
712
713        Parameters
714        ----------
715        clan_id : `int`
716            The clan id.
717
718        Returns
719        -------
720        `aiobungie.Iterator[aiobungie.crates.ClanMember]`
721            An iterator over the found clan admins and founder.
722
723        Raises
724        ------
725        `aiobungie.NotFound`
726            The requested clan was not found.
727        """
728        resp = await self.rest.fetch_clan_admins(clan_id)
729
730        return self.factory.deserialize_clan_members(resp)

Fetch the clan founder and admins.

Parameters
  • clan_id (int): The clan id.
Returns
Raises
async def fetch_groups_for_member( self, member_id: int, member_type: MembershipType | int, /, *, filter: int = 0, group_type: GroupType = <GroupType.CLAN: 1>) -> collections.abc.Sequence[aiobungie.crates.clans.GroupMember]:
732    async def fetch_groups_for_member(
733        self,
734        member_id: int,
735        member_type: enums.MembershipType | int,
736        /,
737        *,
738        filter: int = 0,
739        group_type: enums.GroupType = enums.GroupType.CLAN,
740    ) -> collections.Sequence[clans.GroupMember]:
741        """Fetch information about the groups that a given member has joined.
742
743        Parameters
744        ----------
745        member_id : `int`
746            The member's id
747        member_type : `aiobungie.MembershipType`
748            The member's membership type.
749
750        Other Parameters
751        ----------------
752        filter : `int`
753            Filter apply to list of joined groups. This Default to `0`
754        group_type : `aiobungie.GroupType`
755            The group's type.
756            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
757
758        Returns
759        -------
760        `collections.Sequence[aiobungie.crates.GroupMember]`
761            A sequence of joined groups for the fetched member.
762        """
763        resp = await self.rest.fetch_groups_for_member(
764            member_id, member_type, filter=filter, group_type=group_type
765        )
766
767        return tuple(
768            self.factory.deserialize_group_member(group) for group in resp["results"]
769        )

Fetch information about the groups that a given member has joined.

Parameters
Other Parameters
Returns
async def fetch_potential_groups_for_member( self, member_id: int, member_type: MembershipType | int, /, *, filter: int = 0, group_type: GroupType | int = <GroupType.CLAN: 1>) -> collections.abc.Sequence[aiobungie.crates.clans.GroupMember]:
771    async def fetch_potential_groups_for_member(
772        self,
773        member_id: int,
774        member_type: enums.MembershipType | int,
775        /,
776        *,
777        filter: int = 0,
778        group_type: enums.GroupType | int = enums.GroupType.CLAN,
779    ) -> collections.Sequence[clans.GroupMember]:
780        """Fetch the potential groups for a clan member.
781
782        Parameters
783        ----------
784        member_id : `int`
785            The member's id
786        member_type : `aiobungie.aiobungie.MembershipType | int`
787            The member's membership type.
788
789        Other Parameters
790        ----------------
791        filter : `int`
792            Filter apply to list of joined groups. This Default to `0`
793        group_type : `aiobungie.aiobungie.GroupType | int`
794            The group's type.
795            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
796
797        Returns
798        -------
799        `collections.Sequence[aiobungie.crates.GroupMember]`
800            A sequence of joined potential groups for the fetched member.
801        """
802        resp = await self.rest.fetch_potential_groups_for_member(
803            member_id, member_type, filter=filter, group_type=group_type
804        )
805
806        return tuple(
807            self.factory.deserialize_group_member(group) for group in resp["results"]
808        )

Fetch the potential groups for a clan member.

Parameters
  • member_id (int): The member's id
  • member_type (aiobungie.aiobungie.MembershipType | int): The member's membership type.
Other Parameters
  • filter (int): Filter apply to list of joined groups. This Default to 0
  • group_type (aiobungie.aiobungie.GroupType | int): The group's type. This is always set to aiobungie.GroupType.CLAN and should not be changed.
Returns
async def fetch_clan_members( self, clan_id: int, /, *, name: str | None = None, type: MembershipType | int = <MembershipType.NONE: 0>) -> Iterator[aiobungie.crates.clans.ClanMember]:
810    async def fetch_clan_members(
811        self,
812        clan_id: int,
813        /,
814        *,
815        name: str | None = None,
816        type: enums.MembershipType | int = enums.MembershipType.NONE,
817    ) -> iterators.Iterator[clans.ClanMember]:
818        """Fetch Bungie clan members.
819
820        Parameters
821        ----------
822        clan_id : `int`
823            The clans id
824
825        Other Parameters
826        ----------------
827        name : `str | None`
828            If provided, Only players matching this name will be returned.
829        type : `aiobungie.MembershipType`
830            An optional clan member's membership type.
831            This parameter is used to filter the returned results
832            by the provided membership, For an example XBox memberships only,
833            Otherwise will return all memberships.
834
835        Returns
836        -------
837        `aiobungie.Iterator[aiobungie.crates.ClanMember]`
838            An iterator over the bungie clan members.
839
840        Raises
841        ------
842        `aiobungie.NotFound`
843            The clan was not found.
844        """
845        resp = await self.rest.fetch_clan_members(clan_id, type=type, name=name)
846
847        return self.factory.deserialize_clan_members(resp)

Fetch Bungie clan members.

Parameters
  • clan_id (int): The clans id
Other Parameters
  • name (str | None): If provided, Only players matching this name will be returned.
  • type (aiobungie.MembershipType): An optional clan member's membership type. This parameter is used to filter the returned results by the provided membership, For an example XBox memberships only, Otherwise will return all memberships.
Returns
Raises
async def fetch_clan_banners(self) -> collections.abc.Sequence[aiobungie.crates.clans.ClanBanner]:
849    async def fetch_clan_banners(self) -> collections.Sequence[clans.ClanBanner]:
850        """Fetch the clan banners.
851
852        Returns
853        -------
854        `collections.Sequence[aiobungie.crates.ClanBanner]`
855            A sequence of the clan banners.
856        """
857        resp = await self.rest.fetch_clan_banners()
858
859        return self.factory.deserialize_clan_banners(resp)

Fetch the clan banners.

Returns
async def kick_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: MembershipType | int) -> aiobungie.crates.clans.Clan:
862    async def kick_clan_member(
863        self,
864        access_token: str,
865        /,
866        group_id: int,
867        membership_id: int,
868        membership_type: enums.MembershipType | int,
869    ) -> clans.Clan:
870        """Kick a member from the clan.
871
872        .. note::
873            This request requires OAuth2: oauth2: `AdminGroups` scope.
874
875        Parameters
876        ----------
877        access_token : `str`
878            The bearer access token associated with the bungie account.
879        group_id: `int`
880            The group id.
881        membership_id : `int`
882            The member id to kick.
883        membership_type : `aiobungie.aiobungie.MembershipType | int`
884            The member's membership type.
885
886        Returns
887        -------
888        `aiobungie.crates.clan.Clan`
889            The clan that the member was kicked from.
890        """
891        resp = await self.rest.kick_clan_member(
892            access_token,
893            group_id=group_id,
894            membership_id=membership_id,
895            membership_type=membership_type,
896        )
897
898        return self.factory.deserialize_clan(resp)

Kick a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to kick.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The member's membership type.
Returns
  • aiobungie.crates.clan.Clan: The clan that the member was kicked from.
async def fetch_clan_weekly_rewards(self, clan_id: int) -> aiobungie.crates.milestones.Milestone:
900    async def fetch_clan_weekly_rewards(self, clan_id: int) -> milestones.Milestone:
901        """Fetch a Bungie clan's weekly reward state.
902
903        Parameters
904        ----------
905        clan_id : `int`
906            The clan's id.
907
908        Returns
909        -------
910        `aiobungie.crates.Milestone`
911            A runtime status of the clan's milestone data.
912        """
913
914        resp = await self.rest.fetch_clan_weekly_rewards(clan_id)
915
916        return self.factory.deserialize_milestone(resp)

Fetch a Bungie clan's weekly reward state.

Parameters
  • clan_id (int): The clan's id.
Returns
async def fetch_inventory_item(self, hash: int, /) -> aiobungie.crates.entity.InventoryEntity:
920    async def fetch_inventory_item(self, hash: int, /) -> entity.InventoryEntity:
921        """Fetch a static inventory item entity given a its hash.
922
923        Parameters
924        ----------
925        hash: `int`
926            Inventory item's hash.
927
928        Returns
929        -------
930        `aiobungie.crates.InventoryEntity`
931            A bungie inventory item.
932        """
933        resp = await self.rest.fetch_inventory_item(hash)
934
935        return self.factory.deserialize_inventory_entity(resp)

Fetch a static inventory item entity given a its hash.

Parameters
  • hash (int): Inventory item's hash.
Returns
async def fetch_objective_entity(self, hash: int, /) -> aiobungie.crates.entity.ObjectiveEntity:
937    async def fetch_objective_entity(self, hash: int, /) -> entity.ObjectiveEntity:
938        """Fetch a Destiny objective entity given a its hash.
939
940        Parameters
941        ----------
942        hash: `int`
943            objective's hash.
944
945        Returns
946        -------
947        `aiobungie.crates.ObjectiveEntity`
948            An objective entity item.
949        """
950        resp = await self.rest.fetch_objective_entity(hash)
951
952        return self.factory.deserialize_objective_entity(resp)

Fetch a Destiny objective entity given a its hash.

Parameters
  • hash (int): objective's hash.
Returns
async def search_entities( self, name: str, entity_type: str, *, page: int = 0) -> Iterator[aiobungie.crates.entity.SearchableEntity]:
954    async def search_entities(
955        self, name: str, entity_type: str, *, page: int = 0
956    ) -> iterators.Iterator[entity.SearchableEntity]:
957        """Search for Destiny2 entities given a name and its type.
958
959        Parameters
960        ----------
961        name : `str`
962            The name of the entity, i.e., Thunderlord, One thousand voices.
963        entity_type : `str`
964            The type of the entity, AKA Definition,
965            For an example `DestinyInventoryItemDefinition` for emblems, weapons, and other inventory items.
966
967        Other Parameters
968        ----------------
969        page : `int`
970            An optional page to return. Default to 0.
971
972        Returns
973        -------
974        `aiobungie.Iterator[aiobungie.crates.SearchableEntity]`
975            An iterator over the found results matching the provided name.
976        """
977        resp = await self.rest.search_entities(name, entity_type, page=page)
978
979        return self.factory.deserialize_inventory_results(resp)

Search for Destiny2 entities given a name and its type.

Parameters
  • name (str): The name of the entity, i.e., Thunderlord, One thousand voices.
  • entity_type (str): The type of the entity, AKA Definition, For an example DestinyInventoryItemDefinition for emblems, weapons, and other inventory items.
Other Parameters
  • page (int): An optional page to return. Default to 0.
Returns
async def fetch_fireteams( self, activity_type: FireteamActivity | int, *, platform: FireteamPlatform | int = <FireteamPlatform.ANY: 0>, language: FireteamLanguage | str = <FireteamLanguage.ALL: >, date_range: int = 0, page: int = 0, slots_filter: int = 0) -> collections.abc.Sequence[aiobungie.crates.fireteams.Fireteam]:
 983    async def fetch_fireteams(
 984        self,
 985        activity_type: fireteams.FireteamActivity | int,
 986        *,
 987        platform: fireteams.FireteamPlatform | int = fireteams.FireteamPlatform.ANY,
 988        language: fireteams.FireteamLanguage | str = fireteams.FireteamLanguage.ALL,
 989        date_range: int = 0,
 990        page: int = 0,
 991        slots_filter: int = 0,
 992    ) -> collections.Sequence[fireteams.Fireteam]:
 993        """Fetch public Bungie fireteams with open slots.
 994
 995        Parameters
 996        ----------
 997        activity_type : `aiobungie.aiobungie.crates.FireteamActivity | int`
 998            The fireteam activity type.
 999
1000        Other Parameters
1001        ----------------
1002        platform : `aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int`
1003            If this is provided. Then the results will be filtered with the given platform.
1004            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1005        language : `aiobungie.crates.fireteams.FireteamLanguage | str`
1006            A locale language to filter the used language in that fireteam.
1007            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1008        date_range : `int`
1009            An integer to filter the date range of the returned fireteams. Defaults to `aiobungie.FireteamDate.ALL`.
1010        page : `int`
1011            The page number. By default its `0` which returns all available activities.
1012        slots_filter : `int`
1013            Filter the returned fireteams based on available slots. Default is `0`
1014
1015        Returns
1016        -------
1017        `collections.Sequence[fireteams.Fireteam]`
1018            A sequence of `aiobungie.crates.Fireteam`.
1019        """
1020
1021        resp = await self.rest.fetch_fireteams(
1022            activity_type,
1023            platform=platform,
1024            language=language,
1025            date_range=date_range,
1026            page=page,
1027            slots_filter=slots_filter,
1028        )
1029
1030        return self.factory.deserialize_fireteams(resp)

Fetch public Bungie fireteams with open slots.

Parameters
  • activity_type (aiobungie.aiobungie.crates.FireteamActivity | int): The fireteam activity type.
Other Parameters
  • platform (aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (FireteamLanguage | str): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • date_range (int): An integer to filter the date range of the returned fireteams. Defaults to aiobungie.FireteamDate.ALL.
  • page (int): The page number. By default its 0 which returns all available activities.
  • slots_filter (int): Filter the returned fireteams based on available slots. Default is 0
Returns
async def fetch_available_clan_fireteams( self, access_token: str, group_id: int, activity_type: FireteamActivity | int, *, platform: FireteamPlatform | int, language: FireteamLanguage | str, date_range: int = 0, page: int = 0, public_only: bool = False, slots_filter: int = 0) -> collections.abc.Sequence[aiobungie.crates.fireteams.Fireteam]:
1032    async def fetch_available_clan_fireteams(
1033        self,
1034        access_token: str,
1035        group_id: int,
1036        activity_type: fireteams.FireteamActivity | int,
1037        *,
1038        platform: fireteams.FireteamPlatform | int,
1039        language: fireteams.FireteamLanguage | str,
1040        date_range: int = 0,
1041        page: int = 0,
1042        public_only: bool = False,
1043        slots_filter: int = 0,
1044    ) -> collections.Sequence[fireteams.Fireteam]:
1045        """Fetch a clan's fireteams with open slots.
1046
1047        .. note::
1048            This method requires OAuth2: ReadGroups scope.
1049
1050        Parameters
1051        ----------
1052        access_token : `str`
1053            The bearer access token associated with the bungie account.
1054        group_id : `int`
1055            The group/clan id of the fireteam.
1056        activity_type : `aiobungie.aiobungie.crates.FireteamActivity | int`
1057            The fireteam activity type.
1058
1059        Other Parameters
1060        ----------------
1061        platform : `aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int`
1062            If this is provided. Then the results will be filtered with the given platform.
1063            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1064        language : `aiobungie.crates.fireteams.FireteamLanguage | str`
1065            A locale language to filter the used language in that fireteam.
1066            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1067        date_range : `int`
1068            An integer to filter the date range of the returned fireteams. Defaults to `0`.
1069        page : `int`
1070            The page number. By default its `0` which returns all available activities.
1071        public_only: `bool`
1072            If set to True, Then only public fireteams will be returned.
1073        slots_filter : `int`
1074            Filter the returned fireteams based on available slots. Default is `0`
1075
1076        Returns
1077        -------
1078        `collections.Sequence[aiobungie.crates.Fireteam]`
1079            A sequence of  fireteams found in the clan.
1080            `None` will be returned if nothing was found.
1081        """
1082        resp = await self.rest.fetch_available_clan_fireteams(
1083            access_token,
1084            group_id,
1085            activity_type,
1086            platform=platform,
1087            language=language,
1088            date_range=date_range,
1089            page=page,
1090            public_only=public_only,
1091            slots_filter=slots_filter,
1092        )
1093
1094        return self.factory.deserialize_fireteams(resp)

Fetch a clan's fireteams with open slots.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id of the fireteam.
  • activity_type (aiobungie.aiobungie.crates.FireteamActivity | int): The fireteam activity type.
Other Parameters
  • platform (aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (FireteamLanguage | str): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • date_range (int): An integer to filter the date range of the returned fireteams. Defaults to 0.
  • page (int): The page number. By default its 0 which returns all available activities.
  • public_only (bool): If set to True, Then only public fireteams will be returned.
  • slots_filter (int): Filter the returned fireteams based on available slots. Default is 0
Returns
  • collections.Sequence[aiobungie.crates.Fireteam]: A sequence of fireteams found in the clan. None will be returned if nothing was found.
async def fetch_clan_fireteam( self, access_token: str, fireteam_id: int, group_id: int) -> aiobungie.crates.fireteams.AvailableFireteam:
1096    async def fetch_clan_fireteam(
1097        self, access_token: str, fireteam_id: int, group_id: int
1098    ) -> fireteams.AvailableFireteam:
1099        """Fetch a specific clan fireteam.
1100
1101        .. note::
1102            This method requires OAuth2: ReadGroups scope.
1103
1104        Parameters
1105        ----------
1106        access_token : `str`
1107            The bearer access token associated with the bungie account.
1108        group_id : `int`
1109            The group/clan id to fetch the fireteam from.
1110        fireteam_id : `int`
1111            The fireteam id to fetch.
1112
1113        Returns
1114        -------
1115        `aiobungie.crates.AvailableFireteam`
1116            A sequence of available fireteams objects.
1117        """
1118        resp = await self.rest.fetch_clan_fireteam(access_token, fireteam_id, group_id)
1119
1120        return self.factory.deserialize_available_fireteam(resp)

Fetch a specific clan fireteam.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch the fireteam from.
  • fireteam_id (int): The fireteam id to fetch.
Returns
async def fetch_my_clan_fireteams( self, access_token: str, group_id: int, *, include_closed: bool = True, platform: FireteamPlatform | int, language: FireteamLanguage | str, filtered: bool = True, page: int = 0) -> collections.abc.Sequence[aiobungie.crates.fireteams.AvailableFireteam]:
1122    async def fetch_my_clan_fireteams(
1123        self,
1124        access_token: str,
1125        group_id: int,
1126        *,
1127        include_closed: bool = True,
1128        platform: fireteams.FireteamPlatform | int,
1129        language: fireteams.FireteamLanguage | str,
1130        filtered: bool = True,
1131        page: int = 0,
1132    ) -> collections.Sequence[fireteams.AvailableFireteam]:
1133        """A method that's similar to `fetch_fireteams` but requires OAuth2.
1134
1135        .. note::
1136            This method requires OAuth2: ReadGroups scope.
1137
1138        Parameters
1139        ----------
1140        access_token : str
1141            The bearer access token associated with the bungie account.
1142        group_id : int
1143            The group/clan id to fetch.
1144
1145        Other Parameters
1146        ----------------
1147        include_closed : `bool`
1148            If provided and set to True, It will also return closed fireteams.
1149            If provided and set to False, It will only return public fireteams. Default is True.
1150        platform : aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int
1151            If this is provided. Then the results will be filtered with the given platform.
1152            Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
1153        language : `aiobungie.crates.fireteams.FireteamLanguage | str`
1154            A locale language to filter the used language in that fireteam.
1155            Defaults to aiobungie.crates.FireteamLanguage.ALL
1156        filtered : `bool`
1157            If set to True, it will filter by clan. Otherwise not. Default is True.
1158        page : `int`
1159            The page number. By default its 0 which returns all available activities.
1160
1161        Returns
1162        -------
1163        `collections.Sequence[aiobungie.crates.AvailableFireteam]`
1164            A sequence of available fireteams objects if exists. else `None` will be returned.
1165        """
1166        resp = await self.rest.fetch_my_clan_fireteams(
1167            access_token,
1168            group_id,
1169            include_closed=include_closed,
1170            platform=platform,
1171            language=language,
1172            filtered=filtered,
1173            page=page,
1174        )
1175
1176        return self.factory.deserialize_available_fireteams(resp)

A method that's similar to fetch_fireteams but requires OAuth2.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch.
Other Parameters
  • include_closed (bool): If provided and set to True, It will also return closed fireteams. If provided and set to False, It will only return public fireteams. Default is True.
  • platform (aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (FireteamLanguage | str): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • filtered (bool): If set to True, it will filter by clan. Otherwise not. Default is True.
  • page (int): The page number. By default its 0 which returns all available activities.
Returns
async def fetch_friends( self, access_token: str, /) -> collections.abc.Sequence[aiobungie.crates.friends.Friend]:
1180    async def fetch_friends(
1181        self, access_token: str, /
1182    ) -> collections.Sequence[friends.Friend]:
1183        """Fetch bungie friend list.
1184
1185        .. note::
1186            This requests OAuth2: ReadUserData scope.
1187
1188        Parameters
1189        -----------
1190        access_token : `str`
1191            The bearer access token associated with the bungie account.
1192
1193        Returns
1194        -------
1195        `collections.Sequence[aiobungie.crates.Friend]`
1196            A sequence of the friends associated with that access token.
1197        """
1198
1199        resp = await self.rest.fetch_friends(access_token)
1200
1201        return self.factory.deserialize_friends(resp)

Fetch bungie friend list.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def fetch_friend_requests(self, access_token: str, /) -> aiobungie.crates.friends.FriendRequestView:
1203    async def fetch_friend_requests(
1204        self, access_token: str, /
1205    ) -> friends.FriendRequestView:
1206        """Fetch pending bungie friend requests queue.
1207
1208        .. note::
1209            This requests OAuth2: ReadUserData scope.
1210
1211        Parameters
1212        -----------
1213        access_token : `str`
1214            The bearer access token associated with the bungie account.
1215
1216        Returns
1217        -------
1218        `aiobungie.crates.FriendRequestView`
1219            A friend requests view of that associated access token.
1220        """
1221
1222        resp = await self.rest.fetch_friend_requests(access_token)
1223
1224        return self.factory.deserialize_friend_requests(resp)

Fetch pending bungie friend requests queue.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def fetch_application(self, appid: int, /) -> aiobungie.crates.application.Application:
1228    async def fetch_application(self, appid: int, /) -> application.Application:
1229        """Fetch a Bungie application.
1230
1231        Parameters
1232        -----------
1233        appid: `int`
1234            The application id.
1235
1236        Returns
1237        --------
1238        `aiobungie.crates.Application`
1239            A Bungie application.
1240        """
1241        resp = await self.rest.fetch_application(appid)
1242
1243        return self.factory.deserialize_app(resp)

Fetch a Bungie application.

Parameters
  • appid (int): The application id.
Returns
async def fetch_public_milestone_content( self, milestone_hash: int, /) -> aiobungie.crates.milestones.MilestoneContent:
1247    async def fetch_public_milestone_content(
1248        self, milestone_hash: int, /
1249    ) -> milestones.MilestoneContent:
1250        """Fetch the milestone content given its hash.
1251
1252        Parameters
1253        ----------
1254        milestone_hash : `int`
1255            The milestone hash.
1256
1257        Returns
1258        -------
1259        `aiobungie.crates.milestones.MilestoneContent`
1260            A milestone content object.
1261        """
1262        ...
1263        resp = await self.rest.fetch_public_milestone_content(milestone_hash)
1264        return self.factory.deserialize_public_milestone_content(resp)

Fetch the milestone content given its hash.

Parameters
  • milestone_hash (int): The milestone hash.
Returns
  • aiobungie.crates.milestones.MilestoneContent: A milestone content object.
@typing.final
class ClosedReasons(aiobungie.Flag):
771@typing.final
772class ClosedReasons(Flag):
773    """A Flags enumeration representing the reasons why a person can't join this user's fireteam."""
774
775    NONE = 0
776    MATCHMAKING = 1 << 0
777    LOADING = 1 << 1
778    SOLO = 1 << 2
779    """The activity is required to be played solo."""
780    INTERNAL_REASONS = 1 << 3
781    """
782    The user can't be joined for one of a variety of internal reasons.
783    Basically, the game can't let you join at this time,
784    but for reasons that aren't under the control of this user
785    """
786    DISALLOWED_BY_GAME_STATE = 1 << 4
787    """The user's current activity/quest/other transitory game state is preventing joining."""
788    OFFLINE = 32768
789    """The user appears offline."""

A Flags enumeration representing the reasons why a person can't join this user's fireteam.

NONE = <ClosedReasons.NONE: 0>
MATCHMAKING = <ClosedReasons.MATCHMAKING: 1>
LOADING = <ClosedReasons.LOADING: 2>
SOLO = <ClosedReasons.SOLO: 4>

The activity is required to be played solo.

INTERNAL_REASONS = <ClosedReasons.INTERNAL_REASONS: 8>

The user can't be joined for one of a variety of internal reasons. Basically, the game can't let you join at this time, but for reasons that aren't under the control of this user

DISALLOWED_BY_GAME_STATE = <ClosedReasons.DISALLOWED_BY_GAME_STATE: 16>

The user's current activity/quest/other transitory game state is preventing joining.

OFFLINE = <ClosedReasons.OFFLINE: 32768>

The user appears offline.

Inherited Members
Flag
name
value
@typing.final
class ComponentFields(aiobungie.Enum):
74@typing.final
75class ComponentFields(enums.Enum):
76    """An enum that provides fields found in a base component response."""
77
78    PRIVACY = ComponentPrivacy.NONE
79    DISABLED = False

An enum that provides fields found in a base component response.

PRIVACY = <ComponentFields.PRIVACY: NONE>
DISABLED = <ComponentFields.PRIVACY: NONE>
Inherited Members
Enum
name
value
@typing.final
class ComponentPrivacy(builtins.int, aiobungie.Enum):
65@typing.final
66class ComponentPrivacy(int, enums.Enum):
67    """An enum the provides privacy settings for profile components."""
68
69    NONE = 0
70    PUBLIC = 1
71    PRIVATE = 2

An enum the provides privacy settings for profile components.

NONE = <ComponentPrivacy.NONE: 0>
PUBLIC = <ComponentPrivacy.PUBLIC: 1>
PRIVATE = <ComponentPrivacy.PRIVATE: 2>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ComponentType(aiobungie.Enum):
347@typing.final
348class ComponentType(Enum):
349    """An Enum for Destiny 2 profile Components."""
350
351    NONE = 0
352
353    PROFILE = 100
354    PROFILE_INVENTORIES = 102
355    PROFILE_CURRENCIES = 103
356    PROFILE_PROGRESSION = 104
357    ALL_PROFILES = (
358        PROFILE,
359        PROFILE_INVENTORIES,
360        PROFILE_CURRENCIES,
361        PROFILE_PROGRESSION,
362    )
363    """All profile components."""
364
365    VENDORS = 400
366    VENDOR_SALES = 402
367    VENDOR_RECEIPTS = 101
368    ALL_VENDORS = (VENDORS, VENDOR_RECEIPTS, VENDOR_SALES)
369    """All vendor components."""
370
371    # Items
372    ITEM_INSTANCES = 300
373    ITEM_OBJECTIVES = 301
374    ITEM_PERKS = 302
375    ITEM_RENDER_DATA = 303
376    ITEM_STATS = 304
377    ITEM_SOCKETS = 305
378    ITEM_TALENT_GRINDS = 306
379    ITEM_PLUG_STATES = 308
380    ITEM_PLUG_OBJECTIVES = 309
381    ITEM_REUSABLE_PLUGS = 310
382
383    ALL_ITEMS = (
384        ITEM_PLUG_OBJECTIVES,
385        ITEM_PLUG_STATES,
386        ITEM_SOCKETS,
387        ITEM_INSTANCES,
388        ITEM_OBJECTIVES,
389        ITEM_PERKS,
390        ITEM_RENDER_DATA,
391        ITEM_STATS,
392        ITEM_TALENT_GRINDS,
393        ITEM_REUSABLE_PLUGS,
394    )
395    """All item components."""
396
397    PLATFORM_SILVER = 105
398    KIOSKS = 500
399    CURRENCY_LOOKUPS = 600
400    PRESENTATION_NODES = 700
401    COLLECTIBLES = 800
402    RECORDS = 900
403    TRANSITORY = 1000
404    METRICS = 1100
405    INVENTORIES = 102
406    STRING_VARIABLES = 1200
407    CRAFTABLES = 1300
408
409    CHARACTERS = 200
410    CHARACTER_INVENTORY = 201
411    CHARECTER_PROGRESSION = 202
412    CHARACTER_RENDER_DATA = 203
413    CHARACTER_ACTIVITIES = 204
414    CHARACTER_EQUIPMENT = 205
415    CHARACTER_LOADOUTS = 206
416
417    ALL_CHARACTERS = (
418        CHARACTERS,
419        CHARACTER_INVENTORY,
420        CHARECTER_PROGRESSION,
421        CHARACTER_RENDER_DATA,
422        CHARACTER_ACTIVITIES,
423        CHARACTER_EQUIPMENT,
424        CHARACTER_LOADOUTS,
425        RECORDS,
426    )
427    """All character components."""
428
429    # Ignores: We those are iterables, They're tuples.
430    ALL = (
431        *ALL_PROFILES,  # pyright: ignore[reportGeneralTypeIssues]
432        *ALL_CHARACTERS,  # pyright: ignore[reportGeneralTypeIssues]
433        *ALL_VENDORS,  # pyright: ignore[reportGeneralTypeIssues]
434        *ALL_ITEMS,  # pyright: ignore[reportGeneralTypeIssues]
435        RECORDS,
436        CURRENCY_LOOKUPS,
437        PRESENTATION_NODES,
438        COLLECTIBLES,
439        KIOSKS,
440        METRICS,
441        PLATFORM_SILVER,
442        INVENTORIES,
443        STRING_VARIABLES,
444        TRANSITORY,
445        CRAFTABLES,
446    )
447    """ALl components included."""

An Enum for Destiny 2 profile Components.

NONE = <ComponentType.NONE: 0>
PROFILE = <ComponentType.PROFILE: 100>
PROFILE_INVENTORIES = <ComponentType.PROFILE_INVENTORIES: 102>
PROFILE_CURRENCIES = <ComponentType.PROFILE_CURRENCIES: 103>
PROFILE_PROGRESSION = <ComponentType.PROFILE_PROGRESSION: 104>
ALL_PROFILES = <ComponentType.ALL_PROFILES: (100, 102, 103, 104)>

All profile components.

VENDORS = <ComponentType.VENDORS: 400>
VENDOR_SALES = <ComponentType.VENDOR_SALES: 402>
VENDOR_RECEIPTS = <ComponentType.VENDOR_RECEIPTS: 101>
ALL_VENDORS = <ComponentType.ALL_VENDORS: (400, 101, 402)>

All vendor components.

ITEM_INSTANCES = <ComponentType.ITEM_INSTANCES: 300>
ITEM_OBJECTIVES = <ComponentType.ITEM_OBJECTIVES: 301>
ITEM_PERKS = <ComponentType.ITEM_PERKS: 302>
ITEM_RENDER_DATA = <ComponentType.ITEM_RENDER_DATA: 303>
ITEM_STATS = <ComponentType.ITEM_STATS: 304>
ITEM_SOCKETS = <ComponentType.ITEM_SOCKETS: 305>
ITEM_TALENT_GRINDS = <ComponentType.ITEM_TALENT_GRINDS: 306>
ITEM_PLUG_STATES = <ComponentType.ITEM_PLUG_STATES: 308>
ITEM_PLUG_OBJECTIVES = <ComponentType.ITEM_PLUG_OBJECTIVES: 309>
ITEM_REUSABLE_PLUGS = <ComponentType.ITEM_REUSABLE_PLUGS: 310>
ALL_ITEMS = <ComponentType.ALL_ITEMS: (309, 308, 305, 300, 301, 302, 303, 304, 306, 310)>

All item components.

PLATFORM_SILVER = <ComponentType.PLATFORM_SILVER: 105>
KIOSKS = <ComponentType.KIOSKS: 500>
CURRENCY_LOOKUPS = <ComponentType.CURRENCY_LOOKUPS: 600>
PRESENTATION_NODES = <ComponentType.PRESENTATION_NODES: 700>
COLLECTIBLES = <ComponentType.COLLECTIBLES: 800>
RECORDS = <ComponentType.RECORDS: 900>
TRANSITORY = <ComponentType.TRANSITORY: 1000>
METRICS = <ComponentType.METRICS: 1100>
INVENTORIES = <ComponentType.PROFILE_INVENTORIES: 102>
STRING_VARIABLES = <ComponentType.STRING_VARIABLES: 1200>
CRAFTABLES = <ComponentType.CRAFTABLES: 1300>
CHARACTERS = <ComponentType.CHARACTERS: 200>
CHARACTER_INVENTORY = <ComponentType.CHARACTER_INVENTORY: 201>
CHARECTER_PROGRESSION = <ComponentType.CHARECTER_PROGRESSION: 202>
CHARACTER_RENDER_DATA = <ComponentType.CHARACTER_RENDER_DATA: 203>
CHARACTER_ACTIVITIES = <ComponentType.CHARACTER_ACTIVITIES: 204>
CHARACTER_EQUIPMENT = <ComponentType.CHARACTER_EQUIPMENT: 205>
CHARACTER_LOADOUTS = <ComponentType.CHARACTER_LOADOUTS: 206>
ALL_CHARACTERS = <ComponentType.ALL_CHARACTERS: (200, 201, 202, 203, 204, 205, 206, 900)>

All character components.

ALL = <ComponentType.ALL: (100, 102, 103, 104, 200, 201, 202, 203, 204, 205, 206, 900, 400, 101, 402, 309, 308, 305, 300, 301, 302, 303, 304, 306, 310, 900, 600, 700, 800, 500, 1100, 105, 102, 1200, 1000, 1300)>

ALl components included.

Inherited Members
Enum
name
value
@typing.final
class CredentialType(builtins.int, aiobungie.Enum):
653@typing.final
654class CredentialType(int, Enum):
655    """The types of the accounts system supports at bungie."""
656
657    NONE = 0
658    XUID = 1
659    PSNID = 2
660    WILD = 3
661    FAKE = 4
662    FACEBOOK = 5
663    GOOGLE = 8
664    WINDOWS = 9
665    DEMONID = 10
666    STEAMID = 12
667    BATTLENETID = 14
668    STADIAID = 16
669    TWITCHID = 18

The types of the accounts system supports at bungie.

NONE = <CredentialType.NONE: 0>
XUID = <CredentialType.XUID: 1>
PSNID = <CredentialType.PSNID: 2>
WILD = <CredentialType.WILD: 3>
FAKE = <CredentialType.FAKE: 4>
FACEBOOK = <CredentialType.FACEBOOK: 5>
GOOGLE = <CredentialType.GOOGLE: 8>
WINDOWS = <CredentialType.WINDOWS: 9>
DEMONID = <CredentialType.DEMONID: 10>
STEAMID = <CredentialType.STEAMID: 12>
BATTLENETID = <CredentialType.BATTLENETID: 14>
STADIAID = <CredentialType.STADIAID: 16>
TWITCHID = <CredentialType.TWITCHID: 18>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class DamageType(builtins.int, aiobungie.Enum):
531@typing.final
532class DamageType(int, Enum):
533    """Enums for Destiny Damage types"""
534
535    NONE = 0
536    KINETIC = 1
537    ARC = 2
538    SOLAR = 3
539    VOID = 4
540    RAID = 5
541    """This is a special damage type reserved for some raid activity encounters."""
542    STASIS = 6

Enums for Destiny Damage types

NONE = <DamageType.NONE: 0>
KINETIC = <DamageType.KINETIC: 1>
ARC = <DamageType.ARC: 2>
SOLAR = <DamageType.SOLAR: 3>
VOID = <DamageType.VOID: 4>
RAID = <DamageType.RAID: 5>

This is a special damage type reserved for some raid activity encounters.

STASIS = <DamageType.STASIS: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Difficulty(builtins.int, aiobungie.Enum):
63@typing.final
64class Difficulty(int, enums.Enum):
65    """An enum for activities difficulties."""
66
67    TRIVIAL = 0
68    EASY = 1
69    NORMAL = 2
70    CHALLENGING = 3
71    HARD = 4
72    BRAVE = 5
73    ALMOST_IMPOSSIBLE = 6
74    IMPOSSIBLE = 7

An enum for activities difficulties.

TRIVIAL = <Difficulty.TRIVIAL: 0>
EASY = <Difficulty.EASY: 1>
NORMAL = <Difficulty.NORMAL: 2>
CHALLENGING = <Difficulty.CHALLENGING: 3>
HARD = <Difficulty.HARD: 4>
BRAVE = <Difficulty.BRAVE: 5>
ALMOST_IMPOSSIBLE = <Difficulty.ALMOST_IMPOSSIBLE: 6>
IMPOSSIBLE = <Difficulty.IMPOSSIBLE: 7>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Dungeon(builtins.int, aiobungie.Enum):
149@typing.final
150class Dungeon(int, Enum):
151    """An Enum for all available Dungeon/Like missions in Destiny 2."""
152
153    NORMAL_PRESAGE = 2124066889
154    """Normal Presage"""
155
156    MASTER_PRESAGE = 4212753278
157    """Master Presage"""
158
159    HARBINGER = 1738383283
160    """Harbinger"""
161
162    PROPHECY = 4148187374
163    """Prophecy"""
164
165    MASTER_POH = 785700673
166    """Master Pit of Heresy?"""
167
168    LEGEND_POH = 785700678
169    """Legend Pit of Heresy?"""
170
171    POH = 1375089621
172    """Normal Pit of Heresy."""
173
174    SHATTERED = 2032534090
175    """Shattered Throne"""
176
177    GOA_LEGEND = 4078656646
178    """Grasp of Avarice legend."""
179
180    GOA_MASTER = 3774021532
181    """Grasp of Avarice master."""

An Enum for all available Dungeon/Like missions in Destiny 2.

NORMAL_PRESAGE = <Dungeon.NORMAL_PRESAGE: 2124066889>

Normal Presage

MASTER_PRESAGE = <Dungeon.MASTER_PRESAGE: 4212753278>

Master Presage

HARBINGER = <Dungeon.HARBINGER: 1738383283>

Harbinger

PROPHECY = <Dungeon.PROPHECY: 4148187374>

Prophecy

MASTER_POH = <Dungeon.MASTER_POH: 785700673>

Master Pit of Heresy?

LEGEND_POH = <Dungeon.LEGEND_POH: 785700678>

Legend Pit of Heresy?

POH = <Dungeon.POH: 1375089621>

Normal Pit of Heresy.

SHATTERED = <Dungeon.SHATTERED: 2032534090>

Shattered Throne

GOA_LEGEND = <Dungeon.GOA_LEGEND: 4078656646>

Grasp of Avarice legend.

GOA_MASTER = <Dungeon.GOA_MASTER: 3774021532>

Grasp of Avarice master.

Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class EmptyFactory(aiobungie.Factory):
2420class EmptyFactory(Factory):
2421    """A stand-alone factory that doesn't requires a client instance.
2422
2423    # Example
2424    ---------
2425    ```py
2426    # We'll implement a serializable RESTClient.
2427    @dataclass(slots=True)
2428    class MyClient(aiobungie.traits.Serializable):
2429        rest = aiobungie.RESTClient(env["CLIENT_TOKEN"])
2430        my_name = "Fate怒"
2431        my_code = 4275
2432
2433        # Must implement this one method.
2434        @property
2435        def factory(self) -> aiobungie.EmptyFactory:
2436            # Return an empty factory
2437            return aiobungie.EmptyFactory()
2438
2439        async def my_memberships(self) -> Sequence[aiobungie.crates.DestinyMembership]:
2440            # Note, Do not call methods within objects, Since this is an empty
2441            # factory, The client reference that makes these calls will be `None`.
2442            response = await self.rest.fetch_membership(self.my_name, self.my_code)
2443            return self.factory.deserialize_destiny_memberships(response)
2444
2445
2446        async def main() -> None:
2447            client = MyClient()
2448            async with client.client:
2449                print(await client.my_memberships())
2450
2451    asyncio.run(main())
2452    ```
2453    """
2454
2455    __slots__ = ()
2456
2457    if typing.TYPE_CHECKING:
2458        # We explicitly want this to be `None`.
2459        _net: None
2460
2461    def __init__(self, net: None = None) -> None:
2462        self._net = net

A stand-alone factory that doesn't requires a client instance.

# Example

# We'll implement a serializable RESTClient.
@dataclass(slots=True)
class MyClient(aiobungie.traits.Serializable):
    rest = aiobungie.RESTClient(env["CLIENT_TOKEN"])
    my_name = "Fate怒"
    my_code = 4275

    # Must implement this one method.
    @property
    def factory(self) -> aiobungie.EmptyFactory:
        # Return an empty factory
        return aiobungie.EmptyFactory()

    async def my_memberships(self) -> Sequence[aiobungie.crates.DestinyMembership]:
        # Note, Do not call methods within objects, Since this is an empty
        # factory, The client reference that makes these calls will be `None`.
        response = await self.rest.fetch_membership(self.my_name, self.my_code)
        return self.factory.deserialize_destiny_memberships(response)


    async def main() -> None:
        client = MyClient()
        async with client.client:
            print(await client.my_memberships())

asyncio.run(main())
EmptyFactory(net: None = None)
2461    def __init__(self, net: None = None) -> None:
2462        self._net = net
Inherited Members
Factory
deserialize_bungie_user
deserialize_partial_bungie_user
deserialize_destiny_membership
deserialize_destiny_memberships
deserialize_user
deserialize_searched_user
deserialize_user_credentials
deserialize_user_themes
deserialize_clan
deserialize_clan_member
deserialize_clan_members
deserialize_group_member
deserialize_clan_conversations
deserialize_app_owner
deserialize_app
deserialize_profile
deserialize_profile_item
deserialize_objectives
deserialize_records
deserialize_character_records
deserialize_character_dye
deserialize_character_customization
deserialize_character_minimal_equipments
deserialize_character_render_data
deserialize_available_activity
deserialize_character_activity
deserialize_profile_items
deserialize_progressions
deserialize_milestone
deserialize_characters
deserialize_character
deserialize_character_equipments
deserialize_character_activities
deserialize_characters_render_data
deserialize_character_progressions
deserialize_character_progressions_mapping
deserialize_characters_records
deserialize_profile_records
deserialize_craftables_component
deserialize_components
deserialize_items_component
deserialize_character_component
deserialize_inventory_results
deserialize_inventory_entity
deserialize_objective_entity
deserialize_activity
deserialize_activities
deserialize_extended_weapon_values
deserialize_post_activity_player
deserialize_post_activity
deserialize_aggregated_activity
deserialize_aggregated_activities
deserialize_linked_profiles
deserialize_clan_banners
deserialize_public_milestone_content
deserialize_friend
deserialize_friends
deserialize_friend_requests
deserialize_fireteams
deserialize_fireteam_destiny_users
deserialize_fireteam_members
deserialize_available_fireteam
deserialize_available_fireteams
deserialize_fireteam_party
deserialize_seasonal_artifact
deserialize_profile_progression
deserialize_instanced_item
deserialize_item_energy
deserialize_item_perk
deserialize_item_socket
deserialize_item_stats_view
deserialize_plug_item_state
class Enum(enum.Enum):
69class Enum(__enum.Enum):
70    """Builtin Python enum with extra handlings."""
71
72    @property
73    def name(self) -> str:
74        return self._name_
75
76    @property
77    def value(self) -> typing.Any:
78        return self._value_
79
80    def __str__(self) -> str:
81        return self._name_
82
83    def __repr__(self) -> str:
84        return f"<{type(self).__name__}.{self._name_}: {self._value_!s}>"
85
86    def __int__(self) -> int:
87        return int(self.value)

Builtin Python enum with extra handlings.

name: str
72    @property
73    def name(self) -> str:
74        return self._name_

The name of the Enum member.

value: Any
76    @property
77    def value(self) -> typing.Any:
78        return self._value_

The value of the Enum member.

class Factory(aiobungie.interfaces.factory.FactoryInterface):
  60class Factory(interfaces.FactoryInterface):
  61    """The base deserialization factory class for all aiobungie objects.
  62
  63    This entity factory is used to deserialize JSON responses from the REST client and turning them
  64    into a `aiobungie.crates` Python classes.
  65    """
  66
  67    __slots__ = ("_net",)
  68
  69    def __init__(self, net: traits.Netrunner) -> None:
  70        self._net = net
  71
  72    def deserialize_bungie_user(self, data: typedefs.JSONObject) -> user.BungieUser:
  73        return user.BungieUser(
  74            id=int(data["membershipId"]),
  75            created_at=time.clean_date(data["firstAccess"]),
  76            name=data.get("cachedBungieGlobalDisplayName"),
  77            is_deleted=data["isDeleted"],
  78            about=data["about"],
  79            updated_at=time.clean_date(data["lastUpdate"]),
  80            psn_name=data.get("psnDisplayName", None),
  81            stadia_name=data.get("stadiaDisplayName", None),
  82            steam_name=data.get("steamDisplayName", None),
  83            twitch_name=data.get("twitchDisplayName", None),
  84            blizzard_name=data.get("blizzardDisplayName", None),
  85            status=data["statusText"],
  86            locale=data["locale"],
  87            picture=assets.Image(path=data["profilePicturePath"]),
  88            code=data.get("cachedBungieGlobalDisplayNameCode", None),
  89            unique_name=data.get("uniqueName", None),
  90            theme_id=int(data["profileTheme"]),
  91            show_activity=bool(data["showActivity"]),
  92            theme_name=data["profileThemeName"],
  93            display_title=data["userTitleDisplay"],
  94        )
  95
  96    def deserialize_partial_bungie_user(
  97        self, payload: typedefs.JSONObject
  98    ) -> user.PartialBungieUser:
  99        return user.PartialBungieUser(
 100            net=self._net,
 101            types=tuple(
 102                enums.MembershipType(type_)
 103                for type_ in payload.get("applicableMembershipTypes", ())
 104            ),
 105            name=payload.get("displayName"),
 106            id=int(payload["membershipId"]),
 107            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
 108            is_public=payload["isPublic"],
 109            icon=assets.Image(path=payload.get("iconPath", "")),
 110            type=enums.MembershipType(payload["membershipType"]),
 111        )
 112
 113    def deserialize_destiny_membership(
 114        self, payload: typedefs.JSONObject
 115    ) -> user.DestinyMembership:
 116        name: str | None = None
 117        if (raw_name := payload.get("bungieGlobalDisplayName")) is not None:
 118            name = typedefs.unknown(raw_name)
 119
 120        return user.DestinyMembership(
 121            net=self._net,
 122            id=int(payload["membershipId"]),
 123            name=name,
 124            code=payload.get("bungieGlobalDisplayNameCode", None),
 125            last_seen_name=payload.get("LastSeenDisplayName")
 126            or payload.get("displayName")  # noqa: W503
 127            or "",  # noqa: W503
 128            type=enums.MembershipType(payload["membershipType"]),
 129            is_public=payload["isPublic"],
 130            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
 131            icon=assets.Image(path=payload.get("iconPath", "")),
 132            types=tuple(
 133                enums.MembershipType(type_)
 134                for type_ in payload.get("applicableMembershipTypes", ())
 135            ),
 136        )
 137
 138    def deserialize_destiny_memberships(
 139        self, data: typedefs.JSONArray
 140    ) -> collections.Sequence[user.DestinyMembership]:
 141        return tuple(
 142            self.deserialize_destiny_membership(membership) for membership in data
 143        )
 144
 145    def deserialize_user(self, data: typedefs.JSONObject) -> user.User:
 146        primary_membership_id: int | None = None
 147        if raw_primary_id := data.get("primaryMembershipId"):
 148            primary_membership_id = int(raw_primary_id)
 149
 150        return user.User(
 151            bungie_user=self.deserialize_bungie_user(data["bungieNetUser"]),
 152            memberships=self.deserialize_destiny_memberships(
 153                data["destinyMemberships"]
 154            ),
 155            primary_membership_id=primary_membership_id,
 156        )
 157
 158    def deserialize_searched_user(
 159        self, payload: typedefs.JSONObject
 160    ) -> user.SearchableDestinyUser:
 161        code: int | None = None
 162        if raw_code := payload.get("bungieGlobalDisplayNameCode"):
 163            code = int(raw_code)
 164
 165        bungie_id: int | None = None
 166        if raw_bungie_id := payload.get("bungieNetMembershipId"):
 167            bungie_id = int(raw_bungie_id)
 168
 169        return user.SearchableDestinyUser(
 170            name=typedefs.unknown(payload["bungieGlobalDisplayName"]),
 171            code=code,
 172            bungie_id=bungie_id,
 173            memberships=self.deserialize_destiny_memberships(
 174                payload["destinyMemberships"]
 175            ),
 176        )
 177
 178    def deserialize_user_credentials(
 179        self, payload: typedefs.JSONArray
 180    ) -> collections.Sequence[user.UserCredentials]:
 181        return tuple(
 182            user.UserCredentials(
 183                type=enums.CredentialType(int(creds["credentialType"])),
 184                display_name=creds["credentialDisplayName"],
 185                is_public=creds["isPublic"],
 186                self_as_string=creds.get("credentialAsString"),
 187            )
 188            for creds in payload
 189        )
 190
 191    def deserialize_user_themes(
 192        self, payload: typedefs.JSONArray
 193    ) -> collections.Sequence[user.UserThemes]:
 194        return tuple(
 195            user.UserThemes(
 196                id=int(entry["userThemeId"]),
 197                name=entry["userThemeName"] if "userThemeName" in entry else None,
 198                description=entry["userThemeDescription"]
 199                if "userThemeDescription" in entry
 200                else None,
 201            )
 202            for entry in payload
 203        )
 204
 205    def _deserialize_group_details(
 206        self,
 207        data: typedefs.JSONObject,
 208        current_user_memberships: collections.Mapping[str, clans.ClanMember]
 209        | None = None,
 210        clan_founder: clans.ClanMember | None = None,
 211    ) -> clans.Clan:
 212        features = data["features"]
 213        features_obj = clans.ClanFeatures(
 214            max_members=features["maximumMembers"],
 215            max_membership_types=features["maximumMembershipsOfGroupType"],
 216            capabilities=features["capabilities"],
 217            membership_types=features["membershipTypes"],
 218            invite_permissions=features["invitePermissionOverride"],
 219            update_banner_permissions=features["updateBannerPermissionOverride"],
 220            update_culture_permissions=features["updateCulturePermissionOverride"],
 221            join_level=features["joinLevel"],
 222        )
 223        information: typedefs.JSONObject = data["clanInfo"]
 224        progression: collections.Mapping[int, progressions.Progression] = {
 225            int(prog_hash): self.deserialize_progressions(prog)
 226            for prog_hash, prog in information["d2ClanProgressions"].items()
 227        }
 228
 229        return clans.Clan(
 230            net=self._net,
 231            id=int(data["groupId"]),
 232            name=data["name"],
 233            type=enums.GroupType(data["groupType"]),
 234            created_at=time.clean_date(data["creationDate"]),
 235            member_count=data["memberCount"],
 236            motto=data["motto"],
 237            about=data["about"],
 238            is_public=data["isPublic"],
 239            banner=assets.Image(path=data["bannerPath"]),
 240            avatar=assets.Image(path=data["avatarPath"]),
 241            tags=tuple(data["tags"]),
 242            features=features_obj,
 243            owner=clan_founder,
 244            progressions=progression,
 245            call_sign=information["clanCallsign"],
 246            banner_data=information["clanBannerData"],
 247            chat_security=data["chatSecurity"],
 248            conversation_id=int(data["conversationId"]),
 249            allow_chat=data["allowChat"],
 250            theme=data["theme"],
 251            current_user_membership=current_user_memberships,
 252        )
 253
 254    def deserialize_clan(self, payload: typedefs.JSONObject) -> clans.Clan:
 255        current_user_map: collections.Mapping[str, clans.ClanMember] | None = None
 256        if raw_current_user := payload.get("currentUserMemberMap"):
 257            # This will get populated if only it was a GroupsV2.GroupResponse.
 258            # GroupsV2.GetGroupsForMemberResponse doesn't have this field.
 259            current_user_map = {
 260                membership_type: self.deserialize_clan_member(membership)
 261                for membership_type, membership in raw_current_user.items()
 262            }
 263
 264        return self._deserialize_group_details(
 265            data=payload["detail"],
 266            clan_founder=self.deserialize_clan_member(payload["founder"]),
 267            current_user_memberships=current_user_map,
 268        )
 269
 270    def deserialize_clan_member(self, data: typedefs.JSONObject, /) -> clans.ClanMember:
 271        destiny_user = self.deserialize_destiny_membership(data["destinyUserInfo"])
 272        return clans.ClanMember(
 273            net=self._net,
 274            last_seen_name=destiny_user.last_seen_name,
 275            id=destiny_user.id,
 276            name=destiny_user.name,
 277            icon=destiny_user.icon,
 278            last_online=time.from_timestamp(int(data["lastOnlineStatusChange"])),
 279            group_id=int(data["groupId"]),
 280            joined_at=time.clean_date(data["joinDate"]),
 281            types=destiny_user.types,
 282            is_public=destiny_user.is_public,
 283            type=destiny_user.type,
 284            code=destiny_user.code,
 285            is_online=data["isOnline"],
 286            crossave_override=destiny_user.crossave_override,
 287            bungie_user=self.deserialize_partial_bungie_user(data["bungieNetUserInfo"])
 288            if "bungieNetUserInfo" in data
 289            else None,
 290            member_type=enums.ClanMemberType(int(data["memberType"])),
 291        )
 292
 293    def deserialize_clan_members(
 294        self, data: typedefs.JSONObject, /
 295    ) -> iterators.Iterator[clans.ClanMember]:
 296        return iterators.Iterator(
 297            self.deserialize_clan_member(member) for member in data["results"]
 298        )
 299
 300    def deserialize_group_member(
 301        self, payload: typedefs.JSONObject
 302    ) -> clans.GroupMember:
 303        member = payload["member"]
 304        return clans.GroupMember(
 305            net=self._net,
 306            join_date=time.clean_date(member["joinDate"]),
 307            group_id=int(member["groupId"]),
 308            member_type=enums.ClanMemberType(member["memberType"]),
 309            is_online=member["isOnline"],
 310            last_online=time.from_timestamp(int(member["lastOnlineStatusChange"])),
 311            inactive_memberships=payload.get("areAllMembershipsInactive", None),
 312            member=self.deserialize_destiny_membership(member["destinyUserInfo"]),
 313            group=self._deserialize_group_details(payload["group"]),
 314        )
 315
 316    def _deserialize_clan_conversation(
 317        self, payload: typedefs.JSONObject
 318    ) -> clans.ClanConversation:
 319        return clans.ClanConversation(
 320            net=self._net,
 321            id=int(payload["conversationId"]),
 322            group_id=int(payload["groupId"]),
 323            name=typedefs.unknown(payload["chatName"]),
 324            chat_enabled=payload["chatEnabled"],
 325            security=payload["chatSecurity"],
 326        )
 327
 328    def deserialize_clan_conversations(
 329        self, payload: typedefs.JSONArray
 330    ) -> collections.Sequence[clans.ClanConversation]:
 331        return tuple(self._deserialize_clan_conversation(conv) for conv in payload)
 332
 333    def deserialize_app_owner(
 334        self, payload: typedefs.JSONObject
 335    ) -> application.ApplicationOwner:
 336        return application.ApplicationOwner(
 337            net=self._net,
 338            name=payload.get("bungieGlobalDisplayName"),
 339            id=int(payload["membershipId"]),
 340            type=enums.MembershipType(payload["membershipType"]),
 341            icon=assets.Image(path=payload["iconPath"]),
 342            is_public=payload["isPublic"],
 343            code=payload.get("bungieGlobalDisplayNameCode", None),
 344        )
 345
 346    def deserialize_app(self, payload: typedefs.JSONObject) -> application.Application:
 347        return application.Application(
 348            id=int(payload["applicationId"]),
 349            name=payload["name"],
 350            link=payload["link"],
 351            status=payload["status"],
 352            redirect_url=payload.get("redirectUrl", None),
 353            created_at=time.clean_date(payload["creationDate"]),
 354            published_at=time.clean_date(payload["firstPublished"]),
 355            owner=self.deserialize_app_owner(payload["team"][0]["user"]),
 356            scope=payload.get("scope"),
 357        )
 358
 359    def _set_character_attrs(self, payload: typedefs.JSONObject) -> character.Character:
 360        return character.Character(
 361            net=self._net,
 362            id=int(payload["characterId"]),
 363            gender=enums.Gender(payload["genderType"]),
 364            race=enums.Race(payload["raceType"]),
 365            class_type=enums.Class(payload["classType"]),
 366            emblem=assets.Image(path=payload["emblemBackgroundPath"])
 367            if "emblemBackgroundPath" in payload
 368            else None,
 369            emblem_icon=assets.Image(path=payload["emblemPath"])
 370            if "emblemPath" in payload
 371            else None,
 372            emblem_hash=int(payload["emblemHash"]) if "emblemHash" in payload else None,
 373            last_played=time.clean_date(payload["dateLastPlayed"]),
 374            total_played_time=int(payload["minutesPlayedTotal"]),
 375            member_id=int(payload["membershipId"]),
 376            member_type=enums.MembershipType(payload["membershipType"]),
 377            level=payload["baseCharacterLevel"],
 378            title_hash=payload.get("titleRecordHash", None),
 379            light=payload["light"],
 380            stats={enums.Stat(int(k)): v for k, v in payload["stats"].items()},
 381        )
 382
 383    def deserialize_profile(self, payload: typedefs.JSONObject, /) -> profile.Profile:
 384        payload = payload["data"]
 385        id = int(payload["userInfo"]["membershipId"])
 386        name = payload["userInfo"]["displayName"]
 387        is_public = payload["userInfo"]["isPublic"]
 388        type = enums.MembershipType(payload["userInfo"]["membershipType"])
 389        last_played = time.clean_date(payload["dateLastPlayed"])
 390        character_ids = tuple(int(cid) for cid in payload["characterIds"])
 391        power_cap = payload["currentSeasonRewardPowerCap"]
 392
 393        return profile.Profile(
 394            id=int(id),
 395            name=name,
 396            is_public=is_public,
 397            type=type,
 398            last_played=last_played,
 399            character_ids=character_ids,
 400            power_cap=power_cap,
 401            net=self._net,
 402        )
 403
 404    def deserialize_profile_item(
 405        self, payload: typedefs.JSONObject
 406    ) -> profile.ProfileItemImpl:
 407        instance_id: int | None = None
 408        if raw_instance_id := payload.get("itemInstanceId"):
 409            instance_id = int(raw_instance_id)
 410
 411        version_number: int | None = None
 412        if raw_version := payload.get("versionNumber"):
 413            version_number = int(raw_version)
 414
 415        transfer_status = enums.TransferStatus(payload["transferStatus"])
 416
 417        return profile.ProfileItemImpl(
 418            net=self._net,
 419            hash=payload["itemHash"],
 420            quantity=payload["quantity"],
 421            bind_status=enums.ItemBindStatus(payload["bindStatus"]),
 422            location=enums.ItemLocation(payload["location"]),
 423            bucket=payload["bucketHash"],
 424            transfer_status=transfer_status,
 425            lockable=payload["lockable"],
 426            state=enums.ItemState(payload["state"]),
 427            dismantle_permissions=payload["dismantlePermission"],
 428            is_wrapper=payload["isWrapper"],
 429            instance_id=instance_id,
 430            version_number=version_number,
 431            ornament_id=payload.get("overrideStyleItemHash"),
 432        )
 433
 434    def deserialize_objectives(self, payload: typedefs.JSONObject) -> records.Objective:
 435        return records.Objective(
 436            net=self._net,
 437            hash=payload["objectiveHash"],
 438            visible=payload["visible"],
 439            complete=payload["complete"],
 440            completion_value=payload["completionValue"],
 441            progress=payload.get("progress"),
 442            destination_hash=payload.get("destinationHash"),
 443            activity_hash=payload.get("activityHash"),
 444        )
 445
 446    # TODO: Remove **nodes and get it directly from the payload.
 447    def deserialize_records(
 448        self,
 449        payload: typedefs.JSONObject,
 450        scores: records.RecordScores | None = None,
 451        **nodes: int,
 452    ) -> records.Record:
 453        objectives: collections.Sequence[records.Objective] | None = None
 454        interval_objectives: collections.Sequence[records.Objective] | None = None
 455        record_state: records.RecordState | int
 456
 457        record_state = records.RecordState(payload["state"])
 458
 459        if raw_objs := payload.get("objectives"):
 460            objectives = tuple(self.deserialize_objectives(obj) for obj in raw_objs)
 461
 462        if raw_interval_objs := payload.get("intervalObjectives"):
 463            interval_objectives = tuple(
 464                self.deserialize_objectives(obj) for obj in raw_interval_objs
 465            )
 466
 467        return records.Record(
 468            scores=scores,
 469            categories_node_hash=nodes.get("categories_hash"),
 470            seals_node_hash=nodes.get("seals_hash"),
 471            state=record_state,
 472            objectives=objectives,
 473            interval_objectives=interval_objectives,
 474            redeemed_count=payload.get("intervalsRedeemedCount", 0),
 475            completion_times=payload.get("completedCount", None),
 476            reward_visibility=payload.get("rewardVisibility"),
 477        )
 478
 479    def deserialize_character_records(
 480        self,
 481        payload: typedefs.JSONObject,
 482        scores: records.RecordScores | None = None,
 483        record_hashes: collections.Sequence[int] = (),
 484    ) -> records.CharacterRecord:
 485        record = self.deserialize_records(payload, scores)
 486        return records.CharacterRecord(
 487            scores=scores,
 488            categories_node_hash=record.categories_node_hash,
 489            seals_node_hash=record.seals_node_hash,
 490            state=record.state,
 491            objectives=record.objectives,
 492            interval_objectives=record.interval_objectives,
 493            redeemed_count=payload.get("intervalsRedeemedCount", 0),
 494            completion_times=payload.get("completedCount"),
 495            reward_visibility=payload.get("rewardVisibility"),
 496            record_hashes=record_hashes,
 497        )
 498
 499    def deserialize_character_dye(self, payload: typedefs.JSONObject) -> character.Dye:
 500        return character.Dye(
 501            channel_hash=payload["channelHash"], dye_hash=payload["dyeHash"]
 502        )
 503
 504    def deserialize_character_customization(
 505        self, payload: typedefs.JSONObject
 506    ) -> character.CustomizationOptions:
 507        return character.CustomizationOptions(
 508            personality=payload["personality"],
 509            face=payload["face"],
 510            skin_color=payload["skinColor"],
 511            lip_color=payload["lipColor"],
 512            eye_color=payload["eyeColor"],
 513            hair_colors=payload.get("hairColors", ()),
 514            feature_colors=payload.get("featureColors", ()),
 515            decal_color=payload["decalColor"],
 516            wear_helmet=payload["wearHelmet"],
 517            hair_index=payload["hairIndex"],
 518            feature_index=payload["featureIndex"],
 519            decal_index=payload["decalIndex"],
 520        )
 521
 522    def deserialize_character_minimal_equipments(
 523        self, payload: typedefs.JSONObject
 524    ) -> character.MinimalEquipments:
 525        if raw_dyes := payload.get("dyes"):
 526            dyes = tuple(self.deserialize_character_dye(dye) for dye in raw_dyes)
 527        else:
 528            dyes = ()
 529
 530        return character.MinimalEquipments(
 531            net=self._net, item_hash=payload["itemHash"], dyes=dyes
 532        )
 533
 534    def deserialize_character_render_data(
 535        self, payload: typedefs.JSONObject, /
 536    ) -> character.RenderedData:
 537        return character.RenderedData(
 538            net=self._net,
 539            customization=self.deserialize_character_customization(
 540                payload["customization"]
 541            ),
 542            custom_dyes=tuple(
 543                self.deserialize_character_dye(dye)
 544                for dye in payload["customDyes"]
 545                if dye
 546            ),
 547            equipment=tuple(
 548                self.deserialize_character_minimal_equipments(equipment)
 549                for equipment in payload["peerView"]["equipment"]
 550            ),
 551        )
 552
 553    def deserialize_available_activity(
 554        self, payload: typedefs.JSONObject
 555    ) -> activity.AvailableActivity:
 556        return activity.AvailableActivity(
 557            hash=payload["activityHash"],
 558            is_new=payload["isNew"],
 559            is_completed=payload["isCompleted"],
 560            is_visible=payload["isVisible"],
 561            display_level=payload.get("displayLevel"),
 562            recommended_light=payload.get("recommendedLight"),
 563            difficulty=activity.Difficulty(payload["difficultyTier"]),
 564            can_join=payload["canJoin"],
 565            can_lead=payload["canLead"],
 566        )
 567
 568    def deserialize_character_activity(
 569        self, payload: typedefs.JSONObject
 570    ) -> activity.CharacterActivity:
 571        current_mode: enums.GameMode | None = None
 572        if raw_current_mode := payload.get("currentActivityModeType"):
 573            current_mode = enums.GameMode(raw_current_mode)
 574
 575        if raw_current_modes := payload.get("currentActivityModeTypes"):
 576            current_mode_types = tuple(
 577                enums.GameMode(type_) for type_ in raw_current_modes
 578            )
 579        else:
 580            current_mode_types = ()
 581
 582        return activity.CharacterActivity(
 583            date_started=time.clean_date(payload["dateActivityStarted"]),
 584            current_hash=payload["currentActivityHash"],
 585            current_mode_hash=payload["currentActivityModeHash"],
 586            current_mode=current_mode,
 587            current_mode_hashes=payload.get("currentActivityModeHashes", ()),
 588            current_mode_types=current_mode_types,
 589            current_playlist_hash=payload.get("currentPlaylistActivityHash"),
 590            last_story_hash=payload["lastCompletedStoryHash"],
 591            available_activities=tuple(
 592                self.deserialize_available_activity(activity_)
 593                for activity_ in payload["availableActivities"]
 594            ),
 595        )
 596
 597    def deserialize_profile_items(
 598        self, payload: typedefs.JSONObject, /
 599    ) -> collections.Sequence[profile.ProfileItemImpl]:
 600        return tuple(self.deserialize_profile_item(item) for item in payload["items"])
 601
 602    def _deserialize_node(self, payload: typedefs.JSONObject) -> records.Node:
 603        return records.Node(
 604            state=int(payload["state"]),
 605            objective=self.deserialize_objectives(payload["objective"])
 606            if "objective" in payload
 607            else None,
 608            progress_value=int(payload["progressValue"]),
 609            completion_value=int(payload["completionValue"]),
 610            record_category_score=int(payload["recordCategoryScore"])
 611            if "recordCategoryScore" in payload
 612            else None,
 613        )
 614
 615    @staticmethod
 616    def _deserialize_collectible(payload: typedefs.JSONObject) -> items.Collectible:
 617        recent_collectibles: collections.Collection[int] | None = None
 618        if raw_recent_collectibles := payload.get("recentCollectibleHashes"):
 619            recent_collectibles = tuple(
 620                int(item_hash) for item_hash in raw_recent_collectibles
 621            )
 622
 623        collectibles: dict[int, int] = {}
 624        for item_hash, mapping in payload["collectibles"].items():
 625            collectibles[int(item_hash)] = int(mapping["state"])
 626
 627        return items.Collectible(
 628            recent_collectibles=recent_collectibles,
 629            collectibles=collectibles,
 630            collection_category_hash=int(payload["collectionCategoriesRootNodeHash"]),
 631            collection_badges_hash=int(payload["collectionBadgesRootNodeHash"]),
 632        )
 633
 634    @staticmethod
 635    def _deserialize_currencies(
 636        payload: typedefs.JSONObject,
 637    ) -> collections.Sequence[items.Currency]:
 638        return tuple(
 639            items.Currency(hash=int(item_hash), amount=int(amount))
 640            for item_hash, amount in payload["itemQuantities"].items()
 641        )
 642
 643    def deserialize_progressions(
 644        self, payload: typedefs.JSONObject
 645    ) -> progressions.Progression:
 646        return progressions.Progression(
 647            hash=int(payload["progressionHash"]),
 648            level=int(payload["level"]),
 649            cap=int(payload["levelCap"]),
 650            daily_limit=int(payload["dailyLimit"]),
 651            weekly_limit=int(payload["weeklyLimit"]),
 652            current_progress=int(payload["currentProgress"]),
 653            daily_progress=int(payload["dailyProgress"]),
 654            needed=int(payload["progressToNextLevel"]),
 655            next_level=int(payload["nextLevelAt"]),
 656        )
 657
 658    def _deserialize_factions(
 659        self, payload: typedefs.JSONObject
 660    ) -> progressions.Factions:
 661        progs = self.deserialize_progressions(payload)
 662        return progressions.Factions(
 663            hash=progs.hash,
 664            level=progs.level,
 665            cap=progs.cap,
 666            daily_limit=progs.daily_limit,
 667            weekly_limit=progs.weekly_limit,
 668            current_progress=progs.current_progress,
 669            daily_progress=progs.daily_progress,
 670            needed=progs.needed,
 671            next_level=progs.next_level,
 672            faction_hash=payload["factionHash"],
 673            faction_vendor_hash=payload["factionVendorIndex"],
 674        )
 675
 676    def _deserialize_milestone_available_quest(
 677        self, payload: typedefs.JSONObject
 678    ) -> milestones.MilestoneQuest:
 679        return milestones.MilestoneQuest(
 680            item_hash=payload["questItemHash"],
 681            status=self._deserialize_milestone_quest_status(payload["status"]),
 682        )
 683
 684    def _deserialize_milestone_activity(
 685        self, payload: typedefs.JSONObject
 686    ) -> milestones.MilestoneActivity:
 687        phases: collections.Sequence[milestones.MilestoneActivityPhase] | None = None
 688        if raw_phases := payload.get("phases"):
 689            phases = tuple(
 690                milestones.MilestoneActivityPhase(
 691                    is_completed=obj["complete"], hash=obj["phaseHash"]
 692                )
 693                for obj in raw_phases
 694            )
 695
 696        return milestones.MilestoneActivity(
 697            hash=payload["activityHash"],
 698            challenges=tuple(
 699                self.deserialize_objectives(obj["objective"])
 700                for obj in payload["challenges"]
 701            ),
 702            modifier_hashes=payload.get("modifierHashes"),
 703            boolean_options=payload.get("booleanActivityOptions"),
 704            phases=phases,
 705        )
 706
 707    def _deserialize_milestone_quest_status(
 708        self, payload: typedefs.JSONObject
 709    ) -> milestones.QuestStatus:
 710        return milestones.QuestStatus(
 711            net=self._net,
 712            quest_hash=payload["questHash"],
 713            step_hash=payload["stepHash"],
 714            step_objectives=tuple(
 715                self.deserialize_objectives(objective)
 716                for objective in payload["stepObjectives"]
 717            ),
 718            is_tracked=payload["tracked"],
 719            is_completed=payload["completed"],
 720            started=payload["started"],
 721            item_instance_id=payload["itemInstanceId"],
 722            vendor_hash=payload.get("vendorHash"),
 723            is_redeemed=payload["redeemed"],
 724        )
 725
 726    def _deserialize_milestone_rewards(
 727        self, payload: typedefs.JSONObject
 728    ) -> milestones.MilestoneReward:
 729        return milestones.MilestoneReward(
 730            category_hash=payload["rewardCategoryHash"],
 731            entries=tuple(
 732                milestones.MilestoneRewardEntry(
 733                    entry_hash=entry["rewardEntryHash"],
 734                    is_earned=entry["earned"],
 735                    is_redeemed=entry["redeemed"],
 736                )
 737                for entry in payload["entries"]
 738            ),
 739        )
 740
 741    def deserialize_milestone(
 742        self, payload: typedefs.JSONObject
 743    ) -> milestones.Milestone:
 744        start_date: datetime.datetime | None = None
 745        if raw_start_date := payload.get("startDate"):
 746            start_date = time.clean_date(raw_start_date)
 747
 748        end_date: datetime.datetime | None = None
 749        if raw_end_date := payload.get("endDate"):
 750            end_date = time.clean_date(raw_end_date)
 751
 752        rewards: collections.Collection[milestones.MilestoneReward] | None = None
 753        if raw_rewards := payload.get("rewards"):
 754            rewards = tuple(
 755                self._deserialize_milestone_rewards(reward) for reward in raw_rewards
 756            )
 757
 758        activities: collections.Sequence[milestones.MilestoneActivity] | None = None
 759        if raw_activities := payload.get("activities"):
 760            activities = tuple(
 761                self._deserialize_milestone_activity(active)
 762                for active in raw_activities
 763            )
 764
 765        quests: collections.Sequence[milestones.MilestoneQuest] | None = None
 766        if raw_quests := payload.get("availableQuests"):
 767            quests = tuple(
 768                self._deserialize_milestone_available_quest(quest)
 769                for quest in raw_quests
 770            )
 771
 772        vendors: collections.Sequence[milestones.MilestoneVendor] | None = None
 773        if raw_vendors := payload.get("vendors"):
 774            vendors = tuple(
 775                milestones.MilestoneVendor(
 776                    vendor_hash=vendor["vendorHash"],
 777                    preview_itemhash=vendor.get("previewItemHash"),
 778                )
 779                for vendor in raw_vendors
 780            )
 781
 782        return milestones.Milestone(
 783            hash=payload["milestoneHash"],
 784            start_date=start_date,
 785            end_date=end_date,
 786            order=payload["order"],
 787            rewards=rewards,
 788            available_quests=quests,
 789            activities=activities,
 790            vendors=vendors,
 791        )
 792
 793    def _deserialize_artifact_tiers(
 794        self, payload: typedefs.JSONObject
 795    ) -> season.ArtifactTier:
 796        return season.ArtifactTier(
 797            hash=payload["tierHash"],
 798            is_unlocked=payload["isUnlocked"],
 799            points_to_unlock=payload["pointsToUnlock"],
 800            items=tuple(
 801                season.ArtifactTierItem(
 802                    hash=item["itemHash"], is_active=item["isActive"]
 803                )
 804                for item in payload["items"]
 805            ),
 806        )
 807
 808    def deserialize_characters(
 809        self, payload: typedefs.JSONObject
 810    ) -> collections.Mapping[int, character.Character]:
 811        return {
 812            int(char_id): self._set_character_attrs(char)
 813            for char_id, char in payload["data"].items()
 814        }
 815
 816    def deserialize_character(
 817        self, payload: typedefs.JSONObject
 818    ) -> character.Character:
 819        return self._set_character_attrs(payload)
 820
 821    def deserialize_character_equipments(
 822        self, payload: typedefs.JSONObject
 823    ) -> collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]:
 824        return {
 825            int(char_id): self.deserialize_profile_items(item)
 826            for char_id, item in payload["data"].items()
 827        }
 828
 829    def deserialize_character_activities(
 830        self, payload: typedefs.JSONObject
 831    ) -> collections.Mapping[int, activity.CharacterActivity]:
 832        return {
 833            int(char_id): self.deserialize_character_activity(data)
 834            for char_id, data in payload["data"].items()
 835        }
 836
 837    def deserialize_characters_render_data(
 838        self, payload: typedefs.JSONObject
 839    ) -> collections.Mapping[int, character.RenderedData]:
 840        return {
 841            int(char_id): self.deserialize_character_render_data(data)
 842            for char_id, data in payload["data"].items()
 843        }
 844
 845    def deserialize_character_progressions(
 846        self, payload: typedefs.JSONObject
 847    ) -> character.CharacterProgression:
 848        progressions_ = {
 849            int(prog_id): self.deserialize_progressions(prog)
 850            for prog_id, prog in payload["progressions"].items()
 851        }
 852
 853        factions = {
 854            int(faction_id): self._deserialize_factions(faction)
 855            for faction_id, faction in payload["factions"].items()
 856        }
 857
 858        milestones_ = {
 859            int(milestone_hash): self.deserialize_milestone(milestone)
 860            for milestone_hash, milestone in payload["milestones"].items()
 861        }
 862
 863        uninstanced_item_objectives = {
 864            int(item_hash): [self.deserialize_objectives(ins) for ins in obj]
 865            for item_hash, obj in payload["uninstancedItemObjectives"].items()
 866        }
 867
 868        artifact = payload["seasonalArtifact"]
 869        seasonal_artifact = season.CharacterScopedArtifact(
 870            hash=artifact["artifactHash"],
 871            points_used=artifact["pointsUsed"],
 872            reset_count=artifact["resetCount"],
 873            tiers=tuple(
 874                self._deserialize_artifact_tiers(tier) for tier in artifact["tiers"]
 875            ),
 876        )
 877        checklists = payload["checklists"]
 878
 879        return character.CharacterProgression(
 880            progressions=progressions_,
 881            factions=factions,
 882            checklists=checklists,
 883            milestones=milestones_,
 884            seasonal_artifact=seasonal_artifact,
 885            uninstanced_item_objectives=uninstanced_item_objectives,
 886        )
 887
 888    # fmt: off
 889    def deserialize_character_progressions_mapping(self, payload: typedefs.JSONObject) -> collections.Mapping[int, character.CharacterProgression]:
 890        character_progressions: collections.MutableMapping[int, character.CharacterProgression] = {}
 891        for char_id, data in payload["data"].items():
 892            character_progressions[int(char_id)] = self.deserialize_character_progressions(data)
 893        return character_progressions
 894    # fmt: on
 895
 896    def deserialize_characters_records(
 897        self,
 898        payload: typedefs.JSONObject,
 899    ) -> collections.Mapping[int, records.CharacterRecord]:
 900        return {
 901            int(rec_id): self.deserialize_character_records(
 902                rec, record_hashes=payload.get("featuredRecordHashes", ())
 903            )
 904            for rec_id, rec in payload["records"].items()
 905        }
 906
 907    def deserialize_profile_records(
 908        self, payload: typedefs.JSONObject
 909    ) -> collections.Mapping[int, records.Record]:
 910        raw_profile_records = payload["data"]
 911        scores = records.RecordScores(
 912            current_score=raw_profile_records["score"],
 913            legacy_score=raw_profile_records["legacyScore"],
 914            lifetime_score=raw_profile_records["lifetimeScore"],
 915        )
 916        return {
 917            int(record_id): self.deserialize_records(
 918                record,
 919                scores,
 920                categories_hash=raw_profile_records["recordCategoriesRootNodeHash"],
 921                seals_hash=raw_profile_records["recordSealsRootNodeHash"],
 922            )
 923            for record_id, record in raw_profile_records["records"].items()
 924        }
 925
 926    def _deserialize_craftable_socket_plug(
 927        self, payload: typedefs.JSONObject
 928    ) -> items.CraftableSocketPlug:
 929        return items.CraftableSocketPlug(
 930            item_hash=int(payload["plugItemHash"]),
 931            failed_requirement_indexes=payload.get("failedRequirementIndexes", ()),
 932        )
 933
 934    def _deserialize_craftable_socket(
 935        self, payload: typedefs.JSONObject
 936    ) -> items.CraftableSocket:
 937        if raw_plug := payload.get("plug"):
 938            plugs = tuple(
 939                self._deserialize_craftable_socket_plug(plug) for plug in raw_plug
 940            )
 941        else:
 942            plugs = ()
 943
 944        return items.CraftableSocket(
 945            plug_set_hash=int(payload["plugSetHash"]), plugs=plugs
 946        )
 947
 948    def _deserialize_craftable_item(
 949        self, payload: typedefs.JSONObject
 950    ) -> items.CraftableItem:
 951        return items.CraftableItem(
 952            is_visible=payload["visible"],
 953            failed_requirement_indexes=payload.get("failedRequirementIndexes", ()),
 954            sockets=tuple(
 955                self._deserialize_craftable_socket(socket)
 956                for socket in payload["sockets"]
 957            ),
 958        )
 959
 960    def deserialize_craftables_component(
 961        self, payload: typedefs.JSONObject
 962    ) -> components.CraftablesComponent:
 963        return components.CraftablesComponent(
 964            net=self._net,
 965            craftables={
 966                int(item_id): self._deserialize_craftable_item(item)
 967                for item_id, item in payload["craftables"].items()
 968                if item is not None
 969            },
 970            crafting_root_node_hash=payload["craftingRootNodeHash"],
 971        )
 972
 973    def deserialize_components(  # noqa: C901 Too complex.
 974        self, payload: typedefs.JSONObject
 975    ) -> components.Component:
 976        # Due to how complex this method is, We'll stick to
 977        # typing.Optional here.
 978
 979        profile_: profile.Profile | None = None
 980        if raw_profile := payload.get("profile"):
 981            profile_ = self.deserialize_profile(raw_profile)
 982
 983        profile_progression: profile.ProfileProgression | None = None
 984        if raw_profile_progression := payload.get("profileProgression"):
 985            profile_progression = self.deserialize_profile_progression(
 986                raw_profile_progression
 987            )
 988
 989        profile_currencies: typing.Optional[
 990            collections.Sequence[profile.ProfileItemImpl]
 991        ] = None
 992        if raw_profile_currencies := payload.get("profileCurrencies"):
 993            if "data" in raw_profile_currencies:
 994                profile_currencies = self.deserialize_profile_items(
 995                    raw_profile_currencies["data"]
 996                )
 997
 998        profile_inventories: typing.Optional[
 999            collections.Sequence[profile.ProfileItemImpl]
1000        ] = None
1001        if raw_profile_inventories := payload.get("profileInventory"):
1002            if "data" in raw_profile_inventories:
1003                profile_inventories = self.deserialize_profile_items(
1004                    raw_profile_inventories["data"]
1005                )
1006
1007        profile_records: typing.Optional[
1008            collections.Mapping[int, records.Record]
1009        ] = None
1010
1011        if raw_profile_records_ := payload.get("profileRecords"):
1012            profile_records = self.deserialize_profile_records(raw_profile_records_)
1013
1014        characters: typing.Optional[
1015            collections.Mapping[int, character.Character]
1016        ] = None
1017        if raw_characters := payload.get("characters"):
1018            characters = self.deserialize_characters(raw_characters)
1019
1020        character_records: typing.Optional[
1021            collections.Mapping[int, records.CharacterRecord]
1022        ] = None
1023
1024        if raw_character_records := payload.get("characterRecords"):
1025            # Had to do it in two steps..
1026            to_update = {}
1027            for _, data in raw_character_records["data"].items():
1028                for record_id, record in data.items():
1029                    to_update[record_id] = record
1030
1031            character_records = {
1032                int(rec_id): self.deserialize_character_records(
1033                    rec, record_hashes=to_update.get("featuredRecordHashes", ())
1034                )
1035                for rec_id, rec in to_update["records"].items()
1036            }
1037
1038        character_equipments: typing.Optional[
1039            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1040        ] = None
1041        if raw_character_equips := payload.get("characterEquipment"):
1042            character_equipments = self.deserialize_character_equipments(
1043                raw_character_equips
1044            )
1045
1046        character_inventories: typing.Optional[
1047            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1048        ] = None
1049        if raw_character_inventories := payload.get("characterInventories"):
1050            if "data" in raw_character_inventories:
1051                character_inventories = self.deserialize_character_equipments(
1052                    raw_character_inventories
1053                )
1054
1055        character_activities: typing.Optional[
1056            collections.Mapping[int, activity.CharacterActivity]
1057        ] = None
1058        if raw_char_acts := payload.get("characterActivities"):
1059            character_activities = self.deserialize_character_activities(raw_char_acts)
1060
1061        character_render_data: typing.Optional[
1062            collections.Mapping[int, character.RenderedData]
1063        ] = None
1064        if raw_character_render_data := payload.get("characterRenderData"):
1065            character_render_data = self.deserialize_characters_render_data(
1066                raw_character_render_data
1067            )
1068
1069        character_progressions: typing.Optional[
1070            collections.Mapping[int, character.CharacterProgression]
1071        ] = None
1072
1073        if raw_character_progressions := payload.get("characterProgressions"):
1074            character_progressions = self.deserialize_character_progressions_mapping(
1075                raw_character_progressions
1076            )
1077
1078        profile_string_vars: typing.Optional[collections.Mapping[int, int]] = None
1079        if raw_profile_string_vars := payload.get("profileStringVariables"):
1080            profile_string_vars = raw_profile_string_vars["data"]["integerValuesByHash"]
1081
1082        character_string_vars: typing.Optional[
1083            collections.Mapping[int, collections.Mapping[int, int]]
1084        ] = None
1085        if raw_character_string_vars := payload.get("characterStringVariables"):
1086            character_string_vars = {
1087                int(char_id): data["integerValuesByHash"]
1088                for char_id, data in raw_character_string_vars["data"].items()
1089            }
1090
1091        metrics: typing.Optional[
1092            collections.Sequence[
1093                collections.Mapping[int, tuple[bool, records.Objective | None]]
1094            ]
1095        ] = None
1096        root_node_hash: int | None = None
1097
1098        if raw_metrics := payload.get("metrics"):
1099            root_node_hash = raw_metrics["data"]["metricsRootNodeHash"]
1100            metrics = tuple(
1101                {
1102                    int(metrics_hash): (
1103                        data["invisible"],
1104                        self.deserialize_objectives(data["objectiveProgress"])
1105                        if "objectiveProgress" in data
1106                        else None,
1107                    )
1108                }
1109                for metrics_hash, data in raw_metrics["data"]["metrics"].items()
1110            )
1111        transitory: fireteams.FireteamParty | None = None
1112        if raw_transitory := payload.get("profileTransitoryData"):
1113            if "data" in raw_transitory:
1114                transitory = self.deserialize_fireteam_party(raw_transitory["data"])
1115
1116        item_components: components.ItemsComponent | None = None
1117        if raw_item_components := payload.get("itemComponents"):
1118            item_components = self.deserialize_items_component(raw_item_components)
1119
1120        profile_plugsets: typing.Optional[
1121            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1122        ] = None
1123
1124        if raw_profile_plugs := payload.get("profilePlugSets"):
1125            profile_plugsets = {
1126                int(index): [self.deserialize_plug_item_state(state) for state in data]
1127                for index, data in raw_profile_plugs["data"]["plugs"].items()
1128            }
1129
1130        character_plugsets: typing.Optional[
1131            collections.Mapping[
1132                int, collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1133            ]
1134        ] = None
1135        if raw_char_plugsets := payload.get("characterPlugSets"):
1136            character_plugsets = {
1137                int(char_id): {
1138                    int(index): [
1139                        self.deserialize_plug_item_state(state) for state in data
1140                    ]
1141                    for index, data in inner["plugs"].items()
1142                }
1143                for char_id, inner in raw_char_plugsets["data"].items()
1144            }
1145
1146        character_collectibles: typing.Optional[
1147            collections.Mapping[int, items.Collectible]
1148        ] = None
1149        if raw_character_collectibles := payload.get("characterCollectibles"):
1150            character_collectibles = {
1151                int(char_id): self._deserialize_collectible(data)
1152                for char_id, data in raw_character_collectibles["data"].items()
1153            }
1154
1155        profile_collectibles: items.Collectible | None = None
1156        if raw_profile_collectibles := payload.get("profileCollectibles"):
1157            profile_collectibles = self._deserialize_collectible(
1158                raw_profile_collectibles["data"]
1159            )
1160
1161        profile_nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1162        if raw_profile_nodes := payload.get("profilePresentationNodes"):
1163            profile_nodes = {
1164                int(node_hash): self._deserialize_node(node)
1165                for node_hash, node in raw_profile_nodes["data"]["nodes"].items()
1166            }
1167
1168        character_nodes: typing.Optional[
1169            collections.Mapping[int, collections.Mapping[int, records.Node]]
1170        ] = None
1171        if raw_character_nodes := payload.get("characterPresentationNodes"):
1172            character_nodes = {
1173                int(char_id): {
1174                    int(node_hash): self._deserialize_node(node)
1175                    for node_hash, node in each_character["nodes"].items()
1176                }
1177                for char_id, each_character in raw_character_nodes["data"].items()
1178            }
1179
1180        platform_silver: typing.Optional[
1181            collections.Mapping[str, profile.ProfileItemImpl]
1182        ] = None
1183        if raw_platform_silver := payload.get("platformSilver"):
1184            if "data" in raw_platform_silver:
1185                platform_silver = {
1186                    platform_name: self.deserialize_profile_item(item)
1187                    for platform_name, item in raw_platform_silver["data"][
1188                        "platformSilver"
1189                    ].items()
1190                }
1191
1192        character_currency_lookups: typing.Optional[
1193            collections.Mapping[int, collections.Sequence[items.Currency]]
1194        ] = None
1195        if raw_char_lookups := payload.get("characterCurrencyLookups"):
1196            if "data" in raw_char_lookups:
1197                character_currency_lookups = {
1198                    int(char_id): self._deserialize_currencies(currency)
1199                    for char_id, currency in raw_char_lookups["data"].items()
1200                }
1201
1202        character_craftables: typing.Optional[
1203            collections.Mapping[int, components.CraftablesComponent]
1204        ] = None
1205        if raw_character_craftables := payload.get("characterCraftables"):
1206            if "data" in raw_character_craftables:
1207                character_craftables = {
1208                    int(char_id): self.deserialize_craftables_component(craftable)
1209                    for char_id, craftable in raw_character_craftables["data"].items()
1210                }
1211
1212        return components.Component(
1213            profiles=profile_,
1214            profile_progression=profile_progression,
1215            profile_currencies=profile_currencies,
1216            profile_inventories=profile_inventories,
1217            profile_records=profile_records,
1218            characters=characters,
1219            character_records=character_records,
1220            character_equipments=character_equipments,
1221            character_inventories=character_inventories,
1222            character_activities=character_activities,
1223            character_render_data=character_render_data,
1224            character_progressions=character_progressions,
1225            profile_string_variables=profile_string_vars,
1226            character_string_variables=character_string_vars,
1227            metrics=metrics,
1228            root_node_hash=root_node_hash,
1229            transitory=transitory,
1230            item_components=item_components,
1231            profile_plugsets=profile_plugsets,
1232            character_plugsets=character_plugsets,
1233            character_collectibles=character_collectibles,
1234            profile_collectibles=profile_collectibles,
1235            profile_nodes=profile_nodes,
1236            character_nodes=character_nodes,
1237            platform_silver=platform_silver,
1238            character_currency_lookups=character_currency_lookups,
1239            character_craftables=character_craftables,
1240        )
1241
1242    def deserialize_items_component(
1243        self, payload: typedefs.JSONObject
1244    ) -> components.ItemsComponent:
1245        # Due to how complex this method is, We'll stick to typing.Optional.
1246        instances: typing.Optional[
1247            collections.Sequence[collections.Mapping[int, items.ItemInstance]]
1248        ] = None
1249        if raw_instances := payload.get("instances"):
1250            instances = tuple(
1251                {int(ins_id): self.deserialize_instanced_item(item)}
1252                for ins_id, item in raw_instances["data"].items()
1253            )
1254
1255        render_data: typing.Optional[
1256            collections.Mapping[int, tuple[bool, dict[int, int]]]
1257        ] = None
1258        if raw_render_data := payload.get("renderData"):
1259            render_data = {
1260                int(ins_id): (data["useCustomDyes"], data["artRegions"])
1261                for ins_id, data in raw_render_data["data"].items()
1262            }
1263
1264        stats: typing.Optional[collections.Mapping[int, items.ItemStatsView]] = None
1265        if raw_stats := payload.get("stats"):
1266            stats = {}
1267            for ins_id, stat in raw_stats["data"].items():
1268                for _, items_ in stat.items():
1269                    stats[int(ins_id)] = self.deserialize_item_stats_view(items_)
1270
1271        sockets: typing.Optional[
1272            collections.Mapping[int, collections.Sequence[items.ItemSocket]]
1273        ] = None
1274        if raw_sockets := payload.get("sockets"):
1275            sockets = {
1276                int(ins_id): tuple(
1277                    self.deserialize_item_socket(socket) for socket in item["sockets"]
1278                )
1279                for ins_id, item in raw_sockets["data"].items()
1280            }
1281
1282        objectives: typing.Optional[
1283            collections.Mapping[int, collections.Sequence[records.Objective]]
1284        ] = None
1285        if raw_objectives := payload.get("objectives"):
1286            objectives = {
1287                int(ins_id): tuple(
1288                    self.deserialize_objectives(objective)
1289                    for objective in data["objectives"]
1290                )
1291                for ins_id, data in raw_objectives["data"].items()
1292            }
1293
1294        perks: typing.Optional[
1295            collections.Mapping[int, collections.Collection[items.ItemPerk]]
1296        ] = None
1297        if raw_perks := payload.get("perks"):
1298            perks = {
1299                int(ins_id): tuple(
1300                    self.deserialize_item_perk(perk) for perk in item["perks"]
1301                )
1302                for ins_id, item in raw_perks["data"].items()
1303            }
1304
1305        plug_states: typing.Optional[collections.Sequence[items.PlugItemState]] = None
1306        if raw_plug_states := payload.get("plugStates"):
1307            plug_states = tuple(
1308                self.deserialize_plug_item_state(plug)
1309                for _, plug in raw_plug_states["data"].items()
1310            )
1311
1312        reusable_plugs: typing.Optional[
1313            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1314        ] = None
1315        if raw_re_plugs := payload.get("reusablePlugs"):
1316            reusable_plugs = {
1317                int(ins_id): tuple(
1318                    self.deserialize_plug_item_state(state) for state in inner
1319                )
1320                for ins_id, plug in raw_re_plugs["data"].items()
1321                for inner in tuple(plug["plugs"].values())
1322            }
1323
1324        plug_objectives: typing.Optional[
1325            collections.Mapping[
1326                int, collections.Mapping[int, collections.Collection[records.Objective]]
1327            ]
1328        ] = None
1329        if raw_plug_objectives := payload.get("plugObjectives"):
1330            plug_objectives = {
1331                int(ins_id): {
1332                    int(obj_hash): tuple(
1333                        self.deserialize_objectives(obj) for obj in objs
1334                    )
1335                    for obj_hash, objs in inner["objectivesPerPlug"].items()
1336                }
1337                for ins_id, inner in raw_plug_objectives["data"].items()
1338            }
1339
1340        return components.ItemsComponent(
1341            sockets=sockets,
1342            stats=stats,
1343            render_data=render_data,
1344            instances=instances,
1345            objectives=objectives,
1346            perks=perks,
1347            plug_states=plug_states,
1348            reusable_plugs=reusable_plugs,
1349            plug_objectives=plug_objectives,
1350        )
1351
1352    def deserialize_character_component(
1353        self, payload: typedefs.JSONObject
1354    ) -> components.CharacterComponent:
1355        character_: character.Character | None = None
1356        if raw_singular_character := payload.get("character"):
1357            character_ = self.deserialize_character(raw_singular_character["data"])
1358
1359        inventory: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1360        if raw_inventory := payload.get("inventory"):
1361            if "data" in raw_inventory:
1362                inventory = self.deserialize_profile_items(raw_inventory["data"])
1363
1364        activities: activity.CharacterActivity | None = None
1365        if raw_activities := payload.get("activities"):
1366            activities = self.deserialize_character_activity(raw_activities["data"])
1367
1368        equipment: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1369        if raw_equipments := payload.get("equipment"):
1370            equipment = self.deserialize_profile_items(raw_equipments["data"])
1371
1372        progressions_: character.CharacterProgression | None = None
1373        if raw_progressions := payload.get("progressions"):
1374            progressions_ = self.deserialize_character_progressions(
1375                raw_progressions["data"]
1376            )
1377
1378        render_data: character.RenderedData | None = None
1379        if raw_render_data := payload.get("renderData"):
1380            render_data = self.deserialize_character_render_data(
1381                raw_render_data["data"]
1382            )
1383
1384        character_records: typing.Optional[
1385            collections.Mapping[int, records.CharacterRecord]
1386        ] = None
1387        if raw_char_records := payload.get("records"):
1388            character_records = self.deserialize_characters_records(
1389                raw_char_records["data"]
1390            )
1391
1392        item_components: components.ItemsComponent | None = None
1393        if raw_item_components := payload.get("itemComponents"):
1394            item_components = self.deserialize_items_component(raw_item_components)
1395
1396        nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1397        if raw_nodes := payload.get("presentationNodes"):
1398            nodes = {
1399                int(node_hash): self._deserialize_node(node)
1400                for node_hash, node in raw_nodes["data"]["nodes"].items()
1401            }
1402
1403        collectibles: items.Collectible | None = None
1404        if raw_collectibles := payload.get("collectibles"):
1405            collectibles = self._deserialize_collectible(raw_collectibles["data"])
1406
1407        currency_lookups: typing.Optional[collections.Sequence[items.Currency]] = None
1408        if raw_currencies := payload.get("currencyLookups"):
1409            if "data" in raw_currencies:
1410                currency_lookups = self._deserialize_currencies(raw_currencies)
1411
1412        return components.CharacterComponent(
1413            activities=activities,
1414            equipment=equipment,
1415            inventory=inventory,
1416            progressions=progressions_,
1417            render_data=render_data,
1418            character=character_,
1419            character_records=character_records,
1420            profile_records=None,
1421            item_components=item_components,
1422            currency_lookups=currency_lookups,
1423            collectibles=collectibles,
1424            nodes=nodes,
1425        )
1426
1427    def _set_entity_attrs(
1428        self, payload: typedefs.JSONObject, *, key: str = "displayProperties"
1429    ) -> entity.Entity:
1430        properties = payload[key]
1431        name = typedefs.unknown(properties["name"])
1432        description = typedefs.unknown(properties["description"])
1433
1434        return entity.Entity(
1435            net=self._net,
1436            hash=payload["hash"],
1437            index=payload["index"],
1438            name=name,
1439            description=description,
1440            has_icon=properties["hasIcon"],
1441            icon=assets.Image.default_or_else(properties.get("icon")),
1442        )
1443
1444    def deserialize_inventory_results(
1445        self, payload: typedefs.JSONObject
1446    ) -> iterators.Iterator[entity.SearchableEntity]:
1447        return iterators.Iterator(
1448            [
1449                entity.SearchableEntity(
1450                    net=self._net,
1451                    hash=data["hash"],
1452                    entity_type=data["entityType"],
1453                    weight=data["weight"],
1454                    suggested_words=payload["suggestedWords"],
1455                    name=data["displayProperties"]["name"],
1456                    has_icon=data["displayProperties"]["hasIcon"],
1457                    description=typedefs.unknown(
1458                        data["displayProperties"]["description"]
1459                    ),
1460                    icon=assets.Image(path=data["displayProperties"]["icon"]),
1461                )
1462                for data in payload["results"]["results"]
1463            ]
1464        )
1465
1466    def _deserialize_inventory_item_objects(
1467        self, payload: typedefs.JSONObject
1468    ) -> entity.InventoryEntityObjects:
1469        return entity.InventoryEntityObjects(
1470            action=payload.get("action"),
1471            set_data=payload.get("setData"),
1472            stats=payload.get("stats"),
1473            equipping_block=payload.get("equippingBlock"),
1474            translation_block=payload.get("translationBlock"),
1475            preview=payload.get("preview"),
1476            quality=payload.get("quality"),
1477            value=payload.get("value"),
1478            source_data=payload.get("sourceData"),
1479            objectives=payload.get("objectives"),
1480            plug=payload.get("plug"),
1481            metrics=payload.get("metrics"),
1482            gearset=payload.get("gearset"),
1483            sack=payload.get("sack"),
1484            sockets=payload.get("sockets"),
1485            summary=payload.get("summary"),
1486            talent_gird=payload.get("talentGrid"),
1487            investments_stats=payload.get("investmentStats"),
1488            perks=payload.get("perks"),
1489            animations=payload.get("animations", ()),
1490            links=payload.get("links", ()),
1491        )
1492
1493    def deserialize_inventory_entity(  # noqa: C901 Too complex.
1494        self, payload: typedefs.JSONObject, /
1495    ) -> entity.InventoryEntity:
1496        props = self._set_entity_attrs(payload)
1497        objects = self._deserialize_inventory_item_objects(payload)
1498
1499        collectible_hash: int | None = None
1500        if raw_collectible_hash := payload.get("collectibleHash"):
1501            collectible_hash = int(raw_collectible_hash)
1502
1503        secondary_icon: assets.Image | None = None
1504        if raw_second_icon := payload.get("secondaryIcon"):
1505            secondary_icon = assets.Image(path=raw_second_icon)
1506
1507        secondary_overlay: assets.Image | None = None
1508        if raw_second_overlay := payload.get("secondaryOverlay"):
1509            secondary_overlay = assets.Image(path=raw_second_overlay)
1510
1511        secondary_special: assets.Image | None = None
1512        if raw_second_special := payload.get("secondarySpecial"):
1513            secondary_special = assets.Image(path=raw_second_special)
1514
1515        screenshot: assets.Image | None = None
1516        if raw_screenshot := payload.get("screenshot"):
1517            screenshot = assets.Image(path=raw_screenshot)
1518
1519        watermark_icon: assets.Image | None = None
1520        if raw_watermark_icon := payload.get("iconWatermark"):
1521            watermark_icon = assets.Image(path=raw_watermark_icon)
1522
1523        watermark_shelved: assets.Image | None = None
1524        if raw_watermark_shelved := payload.get("iconWatermarkShelved"):
1525            watermark_shelved = assets.Image(path=raw_watermark_shelved)
1526
1527        about: str | None = None
1528        if raw_about := payload.get("flavorText"):
1529            about = raw_about
1530
1531        ui_item_style: str | None = None
1532        if raw_ui_style := payload.get("uiItemDisplayStyle"):
1533            ui_item_style = raw_ui_style
1534
1535        tier_and_name: str | None = None
1536        if raw_tier_and_name := payload.get("itemTypeAndTierDisplayName"):
1537            tier_and_name = raw_tier_and_name
1538
1539        type_name: str | None = None
1540        if raw_type_name := payload.get("itemTypeDisplayName"):
1541            type_name = raw_type_name
1542
1543        display_source: str | None = None
1544        if raw_display_source := payload.get("displaySource"):
1545            display_source = raw_display_source
1546
1547        lorehash: int | None = None
1548        if raw_lore_hash := payload.get("loreHash"):
1549            lorehash = int(raw_lore_hash)
1550
1551        summary_hash: int | None = None
1552        if raw_summary_hash := payload.get("summaryItemHash"):
1553            summary_hash = raw_summary_hash
1554
1555        breaker_type_hash: int | None = None
1556        if raw_breaker_type_hash := payload.get("breakerTypeHash"):
1557            breaker_type_hash = int(raw_breaker_type_hash)
1558
1559        damage_types: typing.Optional[collections.Sequence[int]] = None
1560        if raw_damage_types := payload.get("damageTypes"):
1561            damage_types = tuple(int(type_) for type_ in raw_damage_types)
1562
1563        damagetype_hashes: typing.Optional[collections.Sequence[int]] = None
1564        if raw_damagetype_hashes := payload.get("damageTypeHashes"):
1565            damagetype_hashes = tuple(int(type_) for type_ in raw_damagetype_hashes)
1566
1567        default_damagetype_hash: int | None = None
1568        if raw_defaultdmg_hash := payload.get("defaultDamageTypeHash"):
1569            default_damagetype_hash = int(raw_defaultdmg_hash)
1570
1571        emblem_objective_hash: int | None = None
1572        if raw_emblem_obj_hash := payload.get("emblemObjectiveHash"):
1573            emblem_objective_hash = int(raw_emblem_obj_hash)
1574
1575        tier_type: enums.TierType | None = None
1576        tier: enums.ItemTier | None = None
1577        bucket_hash: int | None = None
1578        recovery_hash: int | None = None
1579        tier_name: str | None = None
1580        isinstance_item: bool = False
1581        expire_tool_tip: str | None = None
1582        expire_in_orbit_message: str | None = None
1583        suppress_expiration: bool = False
1584        max_stack_size: int | None = None
1585        stack_label: str | None = None
1586
1587        if inventory := payload.get("inventory"):
1588            tier_type = enums.TierType(int(inventory["tierType"]))
1589            tier = enums.ItemTier(int(inventory["tierTypeHash"]))
1590            bucket_hash = int(inventory["bucketTypeHash"])
1591            recovery_hash = int(inventory["recoveryBucketTypeHash"])
1592            tier_name = inventory["tierTypeName"]
1593            isinstance_item = inventory["isInstanceItem"]
1594            suppress_expiration = inventory["suppressExpirationWhenObjectivesComplete"]
1595            max_stack_size = int(inventory["maxStackSize"])
1596
1597            try:
1598                stack_label = inventory["stackUniqueLabel"]
1599            except KeyError:
1600                pass
1601
1602        if "traitHashes" in payload:
1603            trait_hashes = tuple(
1604                int(trait_hash) for trait_hash in payload["traitHashes"]
1605            )
1606        else:
1607            trait_hashes = ()
1608
1609        if "traitIds" in payload:
1610            trait_ids = tuple(trait_id for trait_id in payload["traitIds"])
1611        else:
1612            trait_ids = ()
1613
1614        return entity.InventoryEntity(
1615            net=self._net,
1616            collectible_hash=collectible_hash,
1617            name=props.name,
1618            about=about,
1619            emblem_objective_hash=emblem_objective_hash,
1620            suppress_expiration=suppress_expiration,
1621            max_stack_size=max_stack_size,
1622            stack_label=stack_label,
1623            tier=tier,
1624            tier_type=tier_type,
1625            tier_name=tier_name,
1626            bucket_hash=bucket_hash,
1627            recovery_bucket_hash=recovery_hash,
1628            isinstance_item=isinstance_item,
1629            expire_in_orbit_message=expire_in_orbit_message,
1630            expiration_tooltip=expire_tool_tip,
1631            lore_hash=lorehash,
1632            type_and_tier_name=tier_and_name,
1633            summary_hash=summary_hash,
1634            ui_display_style=ui_item_style,
1635            type_name=type_name,
1636            breaker_type_hash=breaker_type_hash,
1637            description=props.description,
1638            display_source=display_source,
1639            hash=props.hash,
1640            damage_types=damage_types,
1641            index=props.index,
1642            icon=props.icon,
1643            has_icon=props.has_icon,
1644            screenshot=screenshot,
1645            watermark_icon=watermark_icon,
1646            watermark_shelved=watermark_shelved,
1647            secondary_icon=secondary_icon,
1648            secondary_overlay=secondary_overlay,
1649            secondary_special=secondary_special,
1650            type=enums.ItemType(int(payload["itemType"])),
1651            category_hashes=tuple(
1652                int(hash_) for hash_ in payload["itemCategoryHashes"]
1653            ),
1654            item_class=enums.Class(int(payload["classType"])),
1655            sub_type=enums.ItemSubType(int(payload["itemSubType"])),
1656            breaker_type=int(payload["breakerType"]),
1657            default_damagetype=int(payload["defaultDamageType"]),
1658            default_damagetype_hash=default_damagetype_hash,
1659            damagetype_hashes=damagetype_hashes,
1660            tooltip_notifications=payload["tooltipNotifications"],
1661            not_transferable=payload["nonTransferrable"],
1662            allow_actions=payload["allowActions"],
1663            is_equippable=payload["equippable"],
1664            objects=objects,
1665            background_colors=payload.get("backgroundColor", {}),
1666            season_hash=payload.get("seasonHash"),
1667            has_postmaster_effect=payload["doesPostmasterPullHaveSideEffects"],
1668            trait_hashes=trait_hashes,
1669            trait_ids=trait_ids,
1670        )
1671
1672    def deserialize_objective_entity(
1673        self, payload: typedefs.JSONObject, /
1674    ) -> entity.ObjectiveEntity:
1675        props = self._set_entity_attrs(payload)
1676        return entity.ObjectiveEntity(
1677            net=self._net,
1678            hash=props.hash,
1679            index=props.index,
1680            description=props.description,
1681            name=props.name,
1682            has_icon=props.has_icon,
1683            icon=props.icon,
1684            unlock_value_hash=payload["unlockValueHash"],
1685            completion_value=payload["completionValue"],
1686            scope=entity.GatingScope(int(payload["scope"])),
1687            location_hash=payload["locationHash"],
1688            allowed_negative_value=payload["allowNegativeValue"],
1689            allowed_value_change=payload["allowValueChangeWhenCompleted"],
1690            counting_downward=payload["isCountingDownward"],
1691            value_style=entity.ValueUIStyle(int(payload["valueStyle"])),
1692            progress_description=payload["progressDescription"],
1693            perks=payload["perks"],
1694            stats=payload["stats"],
1695            minimum_visibility=payload["minimumVisibilityThreshold"],
1696            allow_over_completion=payload["allowOvercompletion"],
1697            show_value_style=payload["showValueOnComplete"],
1698            display_only_objective=payload["isDisplayOnlyObjective"],
1699            complete_value_style=entity.ValueUIStyle(
1700                int(payload["completedValueStyle"])
1701            ),
1702            progress_value_style=entity.ValueUIStyle(
1703                int(payload["inProgressValueStyle"])
1704            ),
1705            ui_label=payload["uiLabel"],
1706            ui_style=entity.ObjectiveUIStyle(int(payload["uiStyle"])),
1707        )
1708
1709    def _deserialize_activity_values(
1710        self, payload: typedefs.JSONObject, /
1711    ) -> activity.ActivityValues:
1712        team: int | None = None
1713        if raw_team := payload.get("team"):
1714            team = raw_team["basic"]["value"]
1715        return activity.ActivityValues(
1716            assists=payload["assists"]["basic"]["value"],
1717            deaths=payload["deaths"]["basic"]["value"],
1718            kills=payload["kills"]["basic"]["value"],
1719            is_completed=bool(payload["completed"]["basic"]["value"]),
1720            opponents_defeated=payload["opponentsDefeated"]["basic"]["value"],
1721            efficiency=payload["efficiency"]["basic"]["value"],
1722            kd_ratio=payload["killsDeathsRatio"]["basic"]["value"],
1723            kd_assists=payload["killsDeathsAssists"]["basic"]["value"],
1724            score=payload["score"]["basic"]["value"],
1725            duration=payload["activityDurationSeconds"]["basic"]["displayValue"],
1726            team=team,
1727            completion_reason=payload["completionReason"]["basic"]["displayValue"],
1728            fireteam_id=payload["fireteamId"]["basic"]["value"],
1729            start_seconds=payload["startSeconds"]["basic"]["value"],
1730            played_time=payload["timePlayedSeconds"]["basic"]["displayValue"],
1731            player_count=payload["playerCount"]["basic"]["value"],
1732            team_score=payload["teamScore"]["basic"]["value"],
1733        )
1734
1735    def deserialize_activity(
1736        self,
1737        payload: typedefs.JSONObject,
1738        /,
1739    ) -> activity.Activity:
1740        period = time.clean_date(payload["period"])
1741        details = payload["activityDetails"]
1742        ref_id = int(details["referenceId"])
1743        instance_id = int(details["instanceId"])
1744        mode = enums.GameMode(details["mode"])
1745        modes = tuple(enums.GameMode(int(mode_)) for mode_ in details["modes"])
1746        is_private = details["isPrivate"]
1747        membership_type = enums.MembershipType(int(details["membershipType"]))
1748
1749        # Since we're using the same fields for post activity method
1750        # this check is required since post activity doesn't values values
1751        values = self._deserialize_activity_values(payload["values"])
1752
1753        return activity.Activity(
1754            net=self._net,
1755            hash=ref_id,
1756            instance_id=instance_id,
1757            mode=mode,
1758            modes=modes,
1759            is_private=is_private,
1760            membership_type=membership_type,
1761            occurred_at=period,
1762            values=values,
1763        )
1764
1765    def deserialize_activities(
1766        self, payload: typedefs.JSONObject
1767    ) -> iterators.Iterator[activity.Activity]:
1768        return iterators.Iterator(
1769            [
1770                self.deserialize_activity(activity_)
1771                for activity_ in payload["activities"]
1772            ]
1773        )
1774
1775    def deserialize_extended_weapon_values(
1776        self, payload: typedefs.JSONObject
1777    ) -> activity.ExtendedWeaponValues:
1778        assists: int | None = None
1779        if raw_assists := payload["values"].get("uniqueWeaponAssists"):
1780            assists = raw_assists["basic"]["value"]
1781        assists_damage: int | None = None
1782
1783        if raw_assists_damage := payload["values"].get("uniqueWeaponAssistDamage"):
1784            assists_damage = raw_assists_damage["basic"]["value"]
1785
1786        return activity.ExtendedWeaponValues(
1787            reference_id=int(payload["referenceId"]),
1788            kills=payload["values"]["uniqueWeaponKills"]["basic"]["value"],
1789            precision_kills=payload["values"]["uniqueWeaponPrecisionKills"]["basic"][
1790                "value"
1791            ],
1792            assists=assists,
1793            assists_damage=assists_damage,
1794            precision_kills_percentage=(
1795                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"]["value"],
1796                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"][
1797                    "displayValue"
1798                ],
1799            ),
1800        )
1801
1802    def _deserialize_extended_values(
1803        self, payload: typedefs.JSONObject
1804    ) -> activity.ExtendedValues:
1805        if raw_weapons := payload.get("weapons"):
1806            weapons = tuple(
1807                self.deserialize_extended_weapon_values(value) for value in raw_weapons
1808            )
1809        else:
1810            weapons = ()
1811
1812        return activity.ExtendedValues(
1813            precision_kills=payload["values"]["precisionKills"]["basic"]["value"],
1814            grenade_kills=payload["values"]["weaponKillsGrenade"]["basic"]["value"],
1815            melee_kills=payload["values"]["weaponKillsMelee"]["basic"]["value"],
1816            super_kills=payload["values"]["weaponKillsSuper"]["basic"]["value"],
1817            ability_kills=payload["values"]["weaponKillsAbility"]["basic"]["value"],
1818            weapons=weapons,
1819        )
1820
1821    def deserialize_post_activity_player(
1822        self, payload: typedefs.JSONObject, /
1823    ) -> activity.PostActivityPlayer:
1824        player = payload["player"]
1825
1826        class_hash: int | None = None
1827        if (class_hash := player.get("classHash")) is not None:
1828            class_hash = class_hash
1829
1830        race_hash: int | None = None
1831        if (race_hash := player.get("raceHash")) is not None:
1832            race_hash = race_hash
1833
1834        gender_hash: int | None = None
1835        if (gender_hash := player.get("genderHash")) is not None:
1836            gender_hash = gender_hash
1837
1838        character_class: str | None = None
1839        if character_class := player.get("characterClass"):
1840            character_class = character_class
1841
1842        character_level: int | None = None
1843        if (character_level := player.get("characterLevel")) is not None:
1844            character_level = character_level
1845
1846        return activity.PostActivityPlayer(
1847            standing=int(payload["standing"]),
1848            score=int(payload["score"]["basic"]["value"]),
1849            character_id=payload["characterId"],
1850            destiny_user=self.deserialize_destiny_membership(player["destinyUserInfo"]),
1851            character_class=character_class,
1852            character_level=character_level,
1853            race_hash=race_hash,
1854            gender_hash=gender_hash,
1855            class_hash=class_hash,
1856            light_level=int(player["lightLevel"]),
1857            emblem_hash=int(player["emblemHash"]),
1858            values=self._deserialize_activity_values(payload["values"]),
1859            extended_values=self._deserialize_extended_values(payload["extended"]),
1860        )
1861
1862    def _deserialize_post_activity_team(
1863        self, payload: typedefs.JSONObject
1864    ) -> activity.PostActivityTeam:
1865        return activity.PostActivityTeam(
1866            id=payload["teamId"],
1867            is_defeated=bool(payload["standing"]["basic"]["value"]),
1868            score=int(payload["score"]["basic"]["value"]),
1869            name=payload["teamName"],
1870        )
1871
1872    def deserialize_post_activity(
1873        self, payload: typedefs.JSONObject
1874    ) -> activity.PostActivity:
1875        period = time.clean_date(payload["period"])
1876        details = payload["activityDetails"]
1877        ref_id = int(details["referenceId"])
1878        instance_id = int(details["instanceId"])
1879        mode = enums.GameMode(details["mode"])
1880        modes = tuple(enums.GameMode(int(mode_)) for mode_ in details["modes"])
1881        is_private = details["isPrivate"]
1882        membership_type = enums.MembershipType(int(details["membershipType"]))
1883        return activity.PostActivity(
1884            net=self._net,
1885            hash=ref_id,
1886            membership_type=membership_type,
1887            instance_id=instance_id,
1888            mode=mode,
1889            modes=modes,
1890            is_private=is_private,
1891            occurred_at=period,
1892            starting_phase=int(payload["startingPhaseIndex"]),
1893            players=tuple(
1894                self.deserialize_post_activity_player(player)
1895                for player in payload["entries"]
1896            ),
1897            teams=tuple(
1898                self._deserialize_post_activity_team(team) for team in payload["teams"]
1899            ),
1900        )
1901
1902    def _deserialize_aggregated_activity_values(
1903        self, payload: typedefs.JSONObject
1904    ) -> activity.AggregatedActivityValues:
1905        # This ID is always the same for all aggregated values.
1906        activity_id = int(payload["fastestCompletionMsForActivity"]["activityId"])
1907
1908        return activity.AggregatedActivityValues(
1909            id=activity_id,
1910            fastest_completion_time=(
1911                int(payload["fastestCompletionMsForActivity"]["basic"]["value"]),
1912                payload["fastestCompletionMsForActivity"]["basic"]["displayValue"],
1913            ),
1914            completions=int(payload["activityCompletions"]["basic"]["value"]),
1915            kills=int(payload["activityKills"]["basic"]["value"]),
1916            deaths=int(payload["activityDeaths"]["basic"]["value"]),
1917            assists=int(payload["activityAssists"]["basic"]["value"]),
1918            seconds_played=(
1919                int(payload["activitySecondsPlayed"]["basic"]["value"]),
1920                payload["activitySecondsPlayed"]["basic"]["displayValue"],
1921            ),
1922            wins=int(payload["activityWins"]["basic"]["value"]),
1923            goals_missed=int(payload["activityGoalsMissed"]["basic"]["value"]),
1924            special_actions=int(payload["activitySpecialActions"]["basic"]["value"]),
1925            best_goals_hit=int(payload["activityBestGoalsHit"]["basic"]["value"]),
1926            best_single_score=int(
1927                payload["activityBestSingleGameScore"]["basic"]["value"]
1928            ),
1929            goals_hit=int(payload["activityGoalsHit"]["basic"]["value"]),
1930            special_score=int(payload["activitySpecialScore"]["basic"]["value"]),
1931            kd_assists=int(payload["activityKillsDeathsAssists"]["basic"]["value"]),
1932            kd_ratio=float(
1933                payload["activityKillsDeathsAssists"]["basic"]["displayValue"]
1934            ),
1935            precision_kills=int(payload["activityPrecisionKills"]["basic"]["value"]),
1936        )
1937
1938    def deserialize_aggregated_activity(
1939        self, payload: typedefs.JSONObject
1940    ) -> activity.AggregatedActivity:
1941        return activity.AggregatedActivity(
1942            hash=int(payload["activityHash"]),
1943            values=self._deserialize_aggregated_activity_values(payload["values"]),
1944        )
1945
1946    def deserialize_aggregated_activities(
1947        self, payload: typedefs.JSONObject
1948    ) -> iterators.Iterator[activity.AggregatedActivity]:
1949        return iterators.Iterator(
1950            [
1951                self.deserialize_aggregated_activity(activity)
1952                for activity in payload["activities"]
1953            ]
1954        )
1955
1956    def deserialize_linked_profiles(
1957        self, payload: typedefs.JSONObject
1958    ) -> profile.LinkedProfile:
1959        bungie_user = self.deserialize_partial_bungie_user(payload["bnetMembership"])
1960
1961        if raw_profile := payload.get("profiles"):
1962            profiles = tuple(
1963                self.deserialize_destiny_membership(p) for p in raw_profile
1964            )
1965        else:
1966            profiles = ()
1967
1968        error_profiles = ()
1969        if raw_profiles_with_errors := payload.get("profilesWithErrors"):
1970            for raw_error_p in raw_profiles_with_errors:
1971                if "infoCard" in raw_error_p:
1972                    error_profiles = tuple(
1973                        self.deserialize_destiny_membership(error_p)
1974                        for error_p in raw_error_p
1975                    )
1976
1977        return profile.LinkedProfile(
1978            bungie_user=bungie_user,
1979            profiles=profiles,
1980            profiles_with_errors=error_profiles,
1981        )
1982
1983    def deserialize_clan_banners(
1984        self, payload: typedefs.JSONObject
1985    ) -> collections.Sequence[clans.ClanBanner]:
1986        if banners := payload.get("clanBannerDecals"):
1987            banner_obj = tuple(
1988                clans.ClanBanner(
1989                    id=int(k),
1990                    foreground=assets.Image(path=v["foregroundPath"]),
1991                    background=assets.Image(path=v["backgroundPath"]),
1992                )
1993                for k, v in banners.items()
1994            )
1995        else:
1996            banner_obj = ()
1997        return banner_obj
1998
1999    def deserialize_public_milestone_content(
2000        self, payload: typedefs.JSONObject
2001    ) -> milestones.MilestoneContent:
2002        if raw_categories := payload.get("itemCategories"):
2003            items_categories = tuple(
2004                milestones.MilestoneItems(
2005                    title=item["title"], hashes=item["itemHashes"]
2006                )
2007                for item in raw_categories
2008            )
2009        else:
2010            items_categories = ()
2011
2012        return milestones.MilestoneContent(
2013            about=typedefs.unknown(payload["about"]),
2014            status=typedefs.unknown(payload["status"]),
2015            tips=payload.get("tips", ()),
2016            items=items_categories,
2017        )
2018
2019    def deserialize_friend(self, payload: typedefs.JSONObject, /) -> friends.Friend:
2020        bungie_user: user.BungieUser | None = None
2021
2022        if raw_bungie_user := payload.get("bungieNetUser"):
2023            bungie_user = self.deserialize_bungie_user(raw_bungie_user)
2024
2025        return friends.Friend(
2026            net=self._net,
2027            id=int(payload["lastSeenAsMembershipId"]),
2028            name=typedefs.unknown(payload["bungieGlobalDisplayName"]),
2029            code=payload.get("bungieGlobalDisplayNameCode"),
2030            relationship=enums.Relationship(payload["relationship"]),
2031            user=bungie_user,
2032            online_status=enums.Presence(payload["onlineStatus"]),
2033            online_title=payload["onlineTitle"],
2034            type=enums.MembershipType(payload["lastSeenAsBungieMembershipType"]),
2035        )
2036
2037    def deserialize_friends(
2038        self, payload: typedefs.JSONObject
2039    ) -> collections.Sequence[friends.Friend]:
2040        return tuple(self.deserialize_friend(friend) for friend in payload["friends"])
2041
2042    def deserialize_friend_requests(
2043        self, payload: typedefs.JSONObject
2044    ) -> friends.FriendRequestView:
2045        if raw_incoming_requests := payload.get("incomingRequests"):
2046            incoming = tuple(
2047                self.deserialize_friend(incoming_request)
2048                for incoming_request in raw_incoming_requests
2049            )
2050        else:
2051            incoming = ()
2052
2053        if raw_outgoing_requests := payload.get("outgoingRequests"):
2054            outgoing = tuple(
2055                self.deserialize_friend(outgoing_request)
2056                for outgoing_request in raw_outgoing_requests
2057            )
2058        else:
2059            outgoing = ()
2060
2061        return friends.FriendRequestView(incoming=incoming, outgoing=outgoing)
2062
2063    def _set_fireteam_fields(
2064        self, payload: typedefs.JSONObject, total_results: int | None = None
2065    ) -> fireteams.Fireteam:
2066        activity_type = fireteams.FireteamActivity(payload["activityType"])
2067        return fireteams.Fireteam(
2068            id=int(payload["fireteamId"]),
2069            group_id=int(payload["groupId"]),
2070            platform=fireteams.FireteamPlatform(payload["platform"]),
2071            is_immediate=payload["isImmediate"],
2072            activity_type=activity_type,
2073            owner_id=int(payload["ownerMembershipId"]),
2074            player_slot_count=payload["playerSlotCount"],
2075            available_player_slots=payload["availablePlayerSlotCount"],
2076            available_alternate_slots=payload["availableAlternateSlotCount"],
2077            title=payload["title"],
2078            date_created=time.clean_date(payload["dateCreated"]),
2079            is_public=payload["isPublic"],
2080            locale=fireteams.FireteamLanguage(payload["locale"]),
2081            is_valid=payload["isValid"],
2082            last_modified=time.clean_date(payload["datePlayerModified"]),
2083            date_modified=time.clean_date(payload["dateModified"])
2084            if "dateModified" in payload
2085            else None,
2086            scheduled_time=time.clean_date(payload["scheduledTime"])
2087            if "scheduledTime" in payload
2088            else None,
2089            total_results=total_results or 0,
2090        )
2091
2092    def deserialize_fireteams(
2093        self, payload: typedefs.JSONObject
2094    ) -> collections.Sequence[fireteams.Fireteam]:
2095        if "results" in payload:
2096            fireteams_ = tuple(
2097                self._set_fireteam_fields(
2098                    elem, total_results=int(payload["totalResults"])
2099                )
2100                for elem in payload["results"]
2101            )
2102        else:
2103            fireteams_ = ()
2104        return fireteams_
2105
2106    def deserialize_fireteam_destiny_users(
2107        self, payload: typedefs.JSONObject
2108    ) -> fireteams.FireteamUser:
2109        destiny_obj = self.deserialize_destiny_membership(payload)
2110        return fireteams.FireteamUser(
2111            net=self._net,
2112            id=destiny_obj.id,
2113            code=destiny_obj.code,
2114            icon=destiny_obj.icon,
2115            types=destiny_obj.types,
2116            type=destiny_obj.type,
2117            is_public=destiny_obj.is_public,
2118            crossave_override=destiny_obj.crossave_override,
2119            name=destiny_obj.name,
2120            last_seen_name=destiny_obj.last_seen_name,
2121            fireteam_display_name=payload["FireteamDisplayName"],
2122            fireteam_membership_id=enums.MembershipType(
2123                payload["FireteamMembershipType"]
2124            ),
2125        )
2126
2127    def deserialize_fireteam_members(
2128        self, payload: typedefs.JSONObject, *, alternatives: bool = False
2129    ) -> collections.Sequence[fireteams.FireteamMember]:
2130        members_: list[fireteams.FireteamMember] = []
2131        if members := payload.get("Members" if not alternatives else "Alternates"):
2132            for member in members:
2133                bungie_fields = self.deserialize_partial_bungie_user(member)
2134                members_fields = fireteams.FireteamMember(
2135                    destiny_user=self.deserialize_fireteam_destiny_users(member),
2136                    has_microphone=member["hasMicrophone"],
2137                    character_id=int(member["characterId"]),
2138                    date_joined=time.clean_date(member["dateJoined"]),
2139                    last_platform_invite_date=time.clean_date(
2140                        member["lastPlatformInviteAttemptDate"]
2141                    ),
2142                    last_platform_invite_result=int(
2143                        member["lastPlatformInviteAttemptResult"]
2144                    ),
2145                    net=self._net,
2146                    name=bungie_fields.name,
2147                    id=bungie_fields.id,
2148                    icon=bungie_fields.icon,
2149                    is_public=bungie_fields.is_public,
2150                    crossave_override=bungie_fields.crossave_override,
2151                    types=bungie_fields.types,
2152                    type=bungie_fields.type,
2153                )
2154                members_.append(members_fields)
2155        return tuple(members_)
2156
2157    def deserialize_available_fireteam(
2158        self, payload: typedefs.JSONObject
2159    ) -> fireteams.AvailableFireteam:
2160        fields = self._set_fireteam_fields(payload["Summary"])
2161        return fireteams.AvailableFireteam(
2162            id=fields.id,
2163            group_id=fields.group_id,
2164            platform=fields.platform,
2165            activity_type=fields.activity_type,
2166            is_immediate=fields.is_immediate,
2167            is_public=fields.is_public,
2168            is_valid=fields.is_valid,
2169            owner_id=fields.owner_id,
2170            player_slot_count=fields.player_slot_count,
2171            available_player_slots=fields.available_player_slots,
2172            available_alternate_slots=fields.available_alternate_slots,
2173            title=fields.title,
2174            date_created=fields.date_created,
2175            locale=fields.locale,
2176            last_modified=fields.last_modified,
2177            total_results=fields.total_results,
2178            scheduled_time=fields.scheduled_time,
2179            date_modified=fields.date_modified,
2180            members=self.deserialize_fireteam_members(payload),
2181            alternatives=self.deserialize_fireteam_members(payload, alternatives=True),
2182        )
2183
2184    def deserialize_available_fireteams(
2185        self, data: typedefs.JSONObject
2186    ) -> collections.Sequence[fireteams.AvailableFireteam]:
2187        if raw_results := data.get("results"):
2188            fireteam_results = tuple(
2189                self.deserialize_available_fireteam(f) for f in raw_results
2190            )
2191        else:
2192            fireteam_results = ()
2193        return fireteam_results
2194
2195    def deserialize_fireteam_party(
2196        self, payload: typedefs.JSONObject
2197    ) -> fireteams.FireteamParty:
2198        last_destination_hash: int | None = None
2199        if raw_dest_hash := payload.get("lastOrbitedDestinationHash"):
2200            last_destination_hash = int(raw_dest_hash)
2201
2202        return fireteams.FireteamParty(
2203            members=tuple(
2204                self._deserialize_fireteam_party_member(member)
2205                for member in payload["partyMembers"]
2206            ),
2207            activity=self._deserialize_fireteam_party_current_activity(
2208                payload["currentActivity"]
2209            ),
2210            settings=self._deserialize_fireteam_party_settings(payload["joinability"]),
2211            last_destination_hash=last_destination_hash,
2212            tracking=payload["tracking"],
2213        )
2214
2215    def _deserialize_fireteam_party_member(
2216        self, payload: typedefs.JSONObject
2217    ) -> fireteams.FireteamPartyMember:
2218        status = fireteams.FireteamPartyMemberState(payload["status"])
2219
2220        return fireteams.FireteamPartyMember(
2221            membership_id=int(payload["membershipId"]),
2222            emblem_hash=int(payload["emblemHash"]),
2223            status=status,
2224            display_name=payload["displayName"] if payload["displayName"] else None,
2225        )
2226
2227    def _deserialize_fireteam_party_current_activity(
2228        self, payload: typedefs.JSONObject
2229    ) -> fireteams.FireteamPartyCurrentActivity:
2230        start_date: datetime.datetime | None = None
2231        if raw_start_date := payload.get("startTime"):
2232            start_date = time.clean_date(raw_start_date)
2233
2234        end_date: datetime.datetime | None = None
2235        if raw_end_date := payload.get("endTime"):
2236            end_date = time.clean_date(raw_end_date)
2237        return fireteams.FireteamPartyCurrentActivity(
2238            start_time=start_date,
2239            end_time=end_date,
2240            score=float(payload["score"]),
2241            highest_opposing_score=float(payload["highestOpposingFactionScore"]),
2242            opponents_count=int(payload["numberOfOpponents"]),
2243            player_count=int(payload["numberOfPlayers"]),
2244        )
2245
2246    def _deserialize_fireteam_party_settings(
2247        self, payload: typedefs.JSONObject
2248    ) -> fireteams.FireteamPartySettings:
2249        closed_reasons = enums.ClosedReasons(payload["closedReasons"])
2250        return fireteams.FireteamPartySettings(
2251            open_slots=int(payload["openSlots"]),
2252            privacy_setting=enums.PrivacySetting(int(payload["privacySetting"])),
2253            closed_reasons=closed_reasons,
2254        )
2255
2256    def deserialize_seasonal_artifact(
2257        self, payload: typedefs.JSONObject
2258    ) -> season.Artifact:
2259        raw_artifact = payload["seasonalArtifact"]
2260
2261        points = raw_artifact["pointProgression"]
2262        points_prog = progressions.Progression(
2263            hash=points["progressionHash"],
2264            level=points["level"],
2265            cap=points["levelCap"],
2266            daily_limit=points["dailyLimit"],
2267            weekly_limit=points["weeklyLimit"],
2268            current_progress=points["currentProgress"],
2269            daily_progress=points["dailyProgress"],
2270            needed=points["progressToNextLevel"],
2271            next_level=points["nextLevelAt"],
2272        )
2273
2274        bonus = raw_artifact["powerBonusProgression"]
2275        power_bonus_prog = progressions.Progression(
2276            hash=bonus["progressionHash"],
2277            level=bonus["level"],
2278            cap=bonus["levelCap"],
2279            daily_limit=bonus["dailyLimit"],
2280            weekly_limit=bonus["weeklyLimit"],
2281            current_progress=bonus["currentProgress"],
2282            daily_progress=bonus["dailyProgress"],
2283            needed=bonus["progressToNextLevel"],
2284            next_level=bonus["nextLevelAt"],
2285        )
2286        return season.Artifact(
2287            hash=raw_artifact["artifactHash"],
2288            power_bonus=raw_artifact["powerBonus"],
2289            acquired_points=raw_artifact["pointsAcquired"],
2290            bonus=power_bonus_prog,
2291            points=points_prog,
2292        )
2293
2294    def deserialize_profile_progression(
2295        self, payload: typedefs.JSONObject
2296    ) -> profile.ProfileProgression:
2297        return profile.ProfileProgression(
2298            artifact=self.deserialize_seasonal_artifact(payload["data"]),
2299            checklist={
2300                int(check_id): checklists
2301                for check_id, checklists in payload["data"]["checklists"].items()
2302            },
2303        )
2304
2305    def deserialize_instanced_item(
2306        self, payload: typedefs.JSONObject
2307    ) -> items.ItemInstance:
2308        damage_type_hash: int | None = None
2309        if raw_damagetype_hash := payload.get("damageTypeHash"):
2310            damage_type_hash = int(raw_damagetype_hash)
2311
2312        required_hashes: typing.Optional[collections.Collection[int]] = None
2313        if raw_required_hashes := payload.get("unlockHashesRequiredToEquip"):
2314            required_hashes = tuple(int(raw_hash) for raw_hash in raw_required_hashes)
2315
2316        breaker_type: items.ItemBreakerType | None = None
2317        if raw_break_type := payload.get("breakerType"):
2318            breaker_type = items.ItemBreakerType(int(raw_break_type))
2319
2320        breaker_type_hash: int | None = None
2321        if raw_break_type_hash := payload.get("breakerTypeHash"):
2322            breaker_type_hash = int(raw_break_type_hash)
2323
2324        energy: items.ItemEnergy | None = None
2325        if raw_energy := payload.get("energy"):
2326            energy = self.deserialize_item_energy(raw_energy)
2327
2328        primary_stats = None
2329        if raw_primary_stats := payload.get("primaryStat"):
2330            primary_stats = self.deserialize_item_stats_view(raw_primary_stats)
2331
2332        return items.ItemInstance(
2333            damage_type=enums.DamageType(int(payload["damageType"])),
2334            damage_type_hash=damage_type_hash,
2335            primary_stat=primary_stats,
2336            item_level=int(payload["itemLevel"]),
2337            quality=int(payload["quality"]),
2338            is_equipped=payload["isEquipped"],
2339            can_equip=payload["canEquip"],
2340            equip_required_level=int(payload["equipRequiredLevel"]),
2341            required_equip_unlock_hashes=required_hashes,
2342            cant_equip_reason=int(payload["cannotEquipReason"]),
2343            breaker_type=breaker_type,
2344            breaker_type_hash=breaker_type_hash,
2345            energy=energy,
2346        )
2347
2348    def deserialize_item_energy(self, payload: typedefs.JSONObject) -> items.ItemEnergy:
2349        energy_hash: int | None = None
2350        if raw_energy_hash := payload.get("energyTypeHash"):
2351            energy_hash = int(raw_energy_hash)
2352
2353        return items.ItemEnergy(
2354            hash=energy_hash,
2355            type=items.ItemEnergyType(int(payload["energyType"])),
2356            capacity=int(payload["energyCapacity"]),
2357            used_energy=int(payload["energyUsed"]),
2358            unused_energy=int(payload["energyUnused"]),
2359        )
2360
2361    def deserialize_item_perk(self, payload: typedefs.JSONObject) -> items.ItemPerk:
2362        perk_hash: int | None = None
2363        if raw_perk_hash := payload.get("perkHash"):
2364            perk_hash = int(raw_perk_hash)
2365
2366        return items.ItemPerk(
2367            hash=perk_hash,
2368            icon=assets.Image(path=payload["iconPath"]),
2369            is_active=payload["isActive"],
2370            is_visible=payload["visible"],
2371        )
2372
2373    def deserialize_item_socket(self, payload: typedefs.JSONObject) -> items.ItemSocket:
2374        plug_hash: int | None = None
2375        if raw_plug_hash := payload.get("plugHash"):
2376            plug_hash = int(raw_plug_hash)
2377
2378        enable_fail_indexes: collections.Sequence[int] | None = None
2379        if raw_indexes := payload.get("enableFailIndexes"):
2380            enable_fail_indexes = tuple(int(index) for index in raw_indexes)
2381
2382        return items.ItemSocket(
2383            plug_hash=plug_hash,
2384            is_enabled=payload["isEnabled"],
2385            enable_fail_indexes=enable_fail_indexes,
2386            is_visible=payload.get("visible"),
2387        )
2388
2389    def deserialize_item_stats_view(
2390        self, payload: typedefs.JSONObject
2391    ) -> items.ItemStatsView:
2392        return items.ItemStatsView(
2393            stat_hash=payload.get("statHash"), value=payload.get("value")
2394        )
2395
2396    def deserialize_plug_item_state(
2397        self, payload: typedefs.JSONObject
2398    ) -> items.PlugItemState:
2399        item_hash: int | None = None
2400        if raw_item_hash := payload.get("plugItemHash"):
2401            item_hash = int(raw_item_hash)
2402
2403        insert_fail_indexes: collections.Sequence[int] | None = None
2404        if raw_fail_indexes := payload.get("insertFailIndexes"):
2405            insert_fail_indexes = tuple(int(k) for k in raw_fail_indexes)
2406
2407        enable_fail_indexes: collections.Sequence[int] | None = None
2408        if raw_enabled_indexes := payload.get("enableFailIndexes"):
2409            enable_fail_indexes = tuple(int(k) for k in raw_enabled_indexes)
2410
2411        return items.PlugItemState(
2412            item_hash=item_hash,
2413            insert_fail_indexes=insert_fail_indexes,
2414            enable_fail_indexes=enable_fail_indexes,
2415            is_enabled=payload["enabled"],
2416            can_insert=payload["canInsert"],
2417        )

The base deserialization factory class for all aiobungie objects.

This entity factory is used to deserialize JSON responses from the REST client and turning them into a aiobungie.crates Python classes.

Factory(net: aiobungie.traits.Netrunner)
69    def __init__(self, net: traits.Netrunner) -> None:
70        self._net = net
def deserialize_bungie_user( self, data: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.user.BungieUser:
72    def deserialize_bungie_user(self, data: typedefs.JSONObject) -> user.BungieUser:
73        return user.BungieUser(
74            id=int(data["membershipId"]),
75            created_at=time.clean_date(data["firstAccess"]),
76            name=data.get("cachedBungieGlobalDisplayName"),
77            is_deleted=data["isDeleted"],
78            about=data["about"],
79            updated_at=time.clean_date(data["lastUpdate"]),
80            psn_name=data.get("psnDisplayName", None),
81            stadia_name=data.get("stadiaDisplayName", None),
82            steam_name=data.get("steamDisplayName", None),
83            twitch_name=data.get("twitchDisplayName", None),
84            blizzard_name=data.get("blizzardDisplayName", None),
85            status=data["statusText"],
86            locale=data["locale"],
87            picture=assets.Image(path=data["profilePicturePath"]),
88            code=data.get("cachedBungieGlobalDisplayNameCode", None),
89            unique_name=data.get("uniqueName", None),
90            theme_id=int(data["profileTheme"]),
91            show_activity=bool(data["showActivity"]),
92            theme_name=data["profileThemeName"],
93            display_title=data["userTitleDisplay"],
94        )

Deserialize a raw JSON Bungie.net user only payload into a user object.

This only returns the Bungie.net user and not the Destiny memberships.

Parameters
Returns
def deserialize_partial_bungie_user( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.user.PartialBungieUser:
 96    def deserialize_partial_bungie_user(
 97        self, payload: typedefs.JSONObject
 98    ) -> user.PartialBungieUser:
 99        return user.PartialBungieUser(
100            net=self._net,
101            types=tuple(
102                enums.MembershipType(type_)
103                for type_ in payload.get("applicableMembershipTypes", ())
104            ),
105            name=payload.get("displayName"),
106            id=int(payload["membershipId"]),
107            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
108            is_public=payload["isPublic"],
109            icon=assets.Image(path=payload.get("iconPath", "")),
110            type=enums.MembershipType(payload["membershipType"]),
111        )

Deserialize a raw JSON of a partial bungieNetUserInfo.

A partial user is a bungie.net user payload with missing information from the main BungieUser object.

Parameters
Returns
def deserialize_destiny_membership( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.user.DestinyMembership:
113    def deserialize_destiny_membership(
114        self, payload: typedefs.JSONObject
115    ) -> user.DestinyMembership:
116        name: str | None = None
117        if (raw_name := payload.get("bungieGlobalDisplayName")) is not None:
118            name = typedefs.unknown(raw_name)
119
120        return user.DestinyMembership(
121            net=self._net,
122            id=int(payload["membershipId"]),
123            name=name,
124            code=payload.get("bungieGlobalDisplayNameCode", None),
125            last_seen_name=payload.get("LastSeenDisplayName")
126            or payload.get("displayName")  # noqa: W503
127            or "",  # noqa: W503
128            type=enums.MembershipType(payload["membershipType"]),
129            is_public=payload["isPublic"],
130            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
131            icon=assets.Image(path=payload.get("iconPath", "")),
132            types=tuple(
133                enums.MembershipType(type_)
134                for type_ in payload.get("applicableMembershipTypes", ())
135            ),
136        )

Deserialize a raw JSON of destinyUserInfo destiny membership information.

Parameters
Returns
  • aiobungie.crates.user.DestinyMembership: A Destiny 2 membership.
def deserialize_destiny_memberships( self, data: collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]]) -> collections.abc.Sequence[aiobungie.crates.user.DestinyMembership]:
138    def deserialize_destiny_memberships(
139        self, data: typedefs.JSONArray
140    ) -> collections.Sequence[user.DestinyMembership]:
141        return tuple(
142            self.deserialize_destiny_membership(membership) for membership in data
143        )

Deserialize a raw JSON payload/array of destinyUserInfo.

Parameters
Returns
  • collections.Sequence[aiobungie.crates.user.DestinyMembership]: A sequence of Destiny 2 memberships.
def deserialize_user( self, data: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.user.User:
145    def deserialize_user(self, data: typedefs.JSONObject) -> user.User:
146        primary_membership_id: int | None = None
147        if raw_primary_id := data.get("primaryMembershipId"):
148            primary_membership_id = int(raw_primary_id)
149
150        return user.User(
151            bungie_user=self.deserialize_bungie_user(data["bungieNetUser"]),
152            memberships=self.deserialize_destiny_memberships(
153                data["destinyMemberships"]
154            ),
155            primary_membership_id=primary_membership_id,
156        )

Deserialize a raw JSON results of fetched user memberships and Bungie.net user its their id.

Parameters
Returns
def deserialize_searched_user( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.user.SearchableDestinyUser:
158    def deserialize_searched_user(
159        self, payload: typedefs.JSONObject
160    ) -> user.SearchableDestinyUser:
161        code: int | None = None
162        if raw_code := payload.get("bungieGlobalDisplayNameCode"):
163            code = int(raw_code)
164
165        bungie_id: int | None = None
166        if raw_bungie_id := payload.get("bungieNetMembershipId"):
167            bungie_id = int(raw_bungie_id)
168
169        return user.SearchableDestinyUser(
170            name=typedefs.unknown(payload["bungieGlobalDisplayName"]),
171            code=code,
172            bungie_id=bungie_id,
173            memberships=self.deserialize_destiny_memberships(
174                payload["destinyMemberships"]
175            ),
176        )

Deserialize the results of user search details.

Parameters
Returns
def deserialize_user_credentials( self, payload: collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]]) -> collections.abc.Sequence[aiobungie.crates.user.UserCredentials]:
178    def deserialize_user_credentials(
179        self, payload: typedefs.JSONArray
180    ) -> collections.Sequence[user.UserCredentials]:
181        return tuple(
182            user.UserCredentials(
183                type=enums.CredentialType(int(creds["credentialType"])),
184                display_name=creds["credentialDisplayName"],
185                is_public=creds["isPublic"],
186                self_as_string=creds.get("credentialAsString"),
187            )
188            for creds in payload
189        )

Deserialize a JSON array of Bungie user credentials.

Parameters
Returns
def deserialize_user_themes( self, payload: collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]]) -> collections.abc.Sequence[aiobungie.crates.user.UserThemes]:
191    def deserialize_user_themes(
192        self, payload: typedefs.JSONArray
193    ) -> collections.Sequence[user.UserThemes]:
194        return tuple(
195            user.UserThemes(
196                id=int(entry["userThemeId"]),
197                name=entry["userThemeName"] if "userThemeName" in entry else None,
198                description=entry["userThemeDescription"]
199                if "userThemeDescription" in entry
200                else None,
201            )
202            for entry in payload
203        )

Deserialize a raw JSON array of Bungie user themes.

Parameters
Returns
  • collections.Sequence[aiobungie.crates.user.UserThemes]: A sequence of bungie user themes.
def deserialize_clan( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.clans.Clan:
254    def deserialize_clan(self, payload: typedefs.JSONObject) -> clans.Clan:
255        current_user_map: collections.Mapping[str, clans.ClanMember] | None = None
256        if raw_current_user := payload.get("currentUserMemberMap"):
257            # This will get populated if only it was a GroupsV2.GroupResponse.
258            # GroupsV2.GetGroupsForMemberResponse doesn't have this field.
259            current_user_map = {
260                membership_type: self.deserialize_clan_member(membership)
261                for membership_type, membership in raw_current_user.items()
262            }
263
264        return self._deserialize_group_details(
265            data=payload["detail"],
266            clan_founder=self.deserialize_clan_member(payload["founder"]),
267            current_user_memberships=current_user_map,
268        )

Deserialize a raw JSON payload of Bungie clan information.

Parameters
Returns
def deserialize_clan_member( self, data: collections.abc.Mapping[str, typing.Any], /) -> aiobungie.crates.clans.ClanMember:
270    def deserialize_clan_member(self, data: typedefs.JSONObject, /) -> clans.ClanMember:
271        destiny_user = self.deserialize_destiny_membership(data["destinyUserInfo"])
272        return clans.ClanMember(
273            net=self._net,
274            last_seen_name=destiny_user.last_seen_name,
275            id=destiny_user.id,
276            name=destiny_user.name,
277            icon=destiny_user.icon,
278            last_online=time.from_timestamp(int(data["lastOnlineStatusChange"])),
279            group_id=int(data["groupId"]),
280            joined_at=time.clean_date(data["joinDate"]),
281            types=destiny_user.types,
282            is_public=destiny_user.is_public,
283            type=destiny_user.type,
284            code=destiny_user.code,
285            is_online=data["isOnline"],
286            crossave_override=destiny_user.crossave_override,
287            bungie_user=self.deserialize_partial_bungie_user(data["bungieNetUserInfo"])
288            if "bungieNetUserInfo" in data
289            else None,
290            member_type=enums.ClanMemberType(int(data["memberType"])),
291        )

Deserialize a JSON payload of a clan member information.

Parameters
Returns
def deserialize_clan_members( self, data: collections.abc.Mapping[str, typing.Any], /) -> Iterator[aiobungie.crates.clans.ClanMember]:
293    def deserialize_clan_members(
294        self, data: typedefs.JSONObject, /
295    ) -> iterators.Iterator[clans.ClanMember]:
296        return iterators.Iterator(
297            self.deserialize_clan_member(member) for member in data["results"]
298        )

Deserialize a JSON payload of a clan members information.

Parameters
Returns
def deserialize_group_member( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.clans.GroupMember:
300    def deserialize_group_member(
301        self, payload: typedefs.JSONObject
302    ) -> clans.GroupMember:
303        member = payload["member"]
304        return clans.GroupMember(
305            net=self._net,
306            join_date=time.clean_date(member["joinDate"]),
307            group_id=int(member["groupId"]),
308            member_type=enums.ClanMemberType(member["memberType"]),
309            is_online=member["isOnline"],
310            last_online=time.from_timestamp(int(member["lastOnlineStatusChange"])),
311            inactive_memberships=payload.get("areAllMembershipsInactive", None),
312            member=self.deserialize_destiny_membership(member["destinyUserInfo"]),
313            group=self._deserialize_group_details(payload["group"]),
314        )

Deserialize a JSON payload of group information for a member.

Parameters
Returns
def deserialize_clan_conversations( self, payload: collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]]) -> collections.abc.Sequence[aiobungie.crates.clans.ClanConversation]:
328    def deserialize_clan_conversations(
329        self, payload: typedefs.JSONArray
330    ) -> collections.Sequence[clans.ClanConversation]:
331        return tuple(self._deserialize_clan_conversation(conv) for conv in payload)

Deserialize a JSON array of a clan conversations information.

Parameters
Returns
def deserialize_app_owner( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.application.ApplicationOwner:
333    def deserialize_app_owner(
334        self, payload: typedefs.JSONObject
335    ) -> application.ApplicationOwner:
336        return application.ApplicationOwner(
337            net=self._net,
338            name=payload.get("bungieGlobalDisplayName"),
339            id=int(payload["membershipId"]),
340            type=enums.MembershipType(payload["membershipType"]),
341            icon=assets.Image(path=payload["iconPath"]),
342            is_public=payload["isPublic"],
343            code=payload.get("bungieGlobalDisplayNameCode", None),
344        )

Deserialize a JSON payload of Bungie Developer portal application owner information.

Parameters
Returns
  • aiobungie.crates.application.ApplicationOwner: An application owner.
def deserialize_app( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.application.Application:
346    def deserialize_app(self, payload: typedefs.JSONObject) -> application.Application:
347        return application.Application(
348            id=int(payload["applicationId"]),
349            name=payload["name"],
350            link=payload["link"],
351            status=payload["status"],
352            redirect_url=payload.get("redirectUrl", None),
353            created_at=time.clean_date(payload["creationDate"]),
354            published_at=time.clean_date(payload["firstPublished"]),
355            owner=self.deserialize_app_owner(payload["team"][0]["user"]),
356            scope=payload.get("scope"),
357        )

Deserialize a JSON payload of Bungie Developer portal application information.

Parameters
Returns
  • aiobungie.crates.application.Application: An application.
def deserialize_profile( self, payload: collections.abc.Mapping[str, typing.Any], /) -> aiobungie.crates.profile.Profile:
383    def deserialize_profile(self, payload: typedefs.JSONObject, /) -> profile.Profile:
384        payload = payload["data"]
385        id = int(payload["userInfo"]["membershipId"])
386        name = payload["userInfo"]["displayName"]
387        is_public = payload["userInfo"]["isPublic"]
388        type = enums.MembershipType(payload["userInfo"]["membershipType"])
389        last_played = time.clean_date(payload["dateLastPlayed"])
390        character_ids = tuple(int(cid) for cid in payload["characterIds"])
391        power_cap = payload["currentSeasonRewardPowerCap"]
392
393        return profile.Profile(
394            id=int(id),
395            name=name,
396            is_public=is_public,
397            type=type,
398            last_played=last_played,
399            character_ids=character_ids,
400            power_cap=power_cap,
401            net=self._net,
402        )

Deserialize a JSON payload of Bungie.net profile information.

Parameters
Returns
def deserialize_profile_item( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.profile.ProfileItemImpl:
404    def deserialize_profile_item(
405        self, payload: typedefs.JSONObject
406    ) -> profile.ProfileItemImpl:
407        instance_id: int | None = None
408        if raw_instance_id := payload.get("itemInstanceId"):
409            instance_id = int(raw_instance_id)
410
411        version_number: int | None = None
412        if raw_version := payload.get("versionNumber"):
413            version_number = int(raw_version)
414
415        transfer_status = enums.TransferStatus(payload["transferStatus"])
416
417        return profile.ProfileItemImpl(
418            net=self._net,
419            hash=payload["itemHash"],
420            quantity=payload["quantity"],
421            bind_status=enums.ItemBindStatus(payload["bindStatus"]),
422            location=enums.ItemLocation(payload["location"]),
423            bucket=payload["bucketHash"],
424            transfer_status=transfer_status,
425            lockable=payload["lockable"],
426            state=enums.ItemState(payload["state"]),
427            dismantle_permissions=payload["dismantlePermission"],
428            is_wrapper=payload["isWrapper"],
429            instance_id=instance_id,
430            version_number=version_number,
431            ornament_id=payload.get("overrideStyleItemHash"),
432        )

Deserialize a JSON payload of a singular profile component item.

Parameters
Returns
def deserialize_objectives( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.records.Objective:
434    def deserialize_objectives(self, payload: typedefs.JSONObject) -> records.Objective:
435        return records.Objective(
436            net=self._net,
437            hash=payload["objectiveHash"],
438            visible=payload["visible"],
439            complete=payload["complete"],
440            completion_value=payload["completionValue"],
441            progress=payload.get("progress"),
442            destination_hash=payload.get("destinationHash"),
443            activity_hash=payload.get("activityHash"),
444        )

Deserialize a JSON payload of an objective found in a record profile component.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
  • aiobungie.crates.records.Objective: A record objective object.
def deserialize_records( self, payload: collections.abc.Mapping[str, typing.Any], scores: aiobungie.crates.records.RecordScores | None = None, **nodes: int) -> aiobungie.crates.records.Record:
447    def deserialize_records(
448        self,
449        payload: typedefs.JSONObject,
450        scores: records.RecordScores | None = None,
451        **nodes: int,
452    ) -> records.Record:
453        objectives: collections.Sequence[records.Objective] | None = None
454        interval_objectives: collections.Sequence[records.Objective] | None = None
455        record_state: records.RecordState | int
456
457        record_state = records.RecordState(payload["state"])
458
459        if raw_objs := payload.get("objectives"):
460            objectives = tuple(self.deserialize_objectives(obj) for obj in raw_objs)
461
462        if raw_interval_objs := payload.get("intervalObjectives"):
463            interval_objectives = tuple(
464                self.deserialize_objectives(obj) for obj in raw_interval_objs
465            )
466
467        return records.Record(
468            scores=scores,
469            categories_node_hash=nodes.get("categories_hash"),
470            seals_node_hash=nodes.get("seals_hash"),
471            state=record_state,
472            objectives=objectives,
473            interval_objectives=interval_objectives,
474            redeemed_count=payload.get("intervalsRedeemedCount", 0),
475            completion_times=payload.get("completedCount", None),
476            reward_visibility=payload.get("rewardVisibility"),
477        )

Deserialize a JSON object of a profile record component.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON object payload
  • scores (records.RecordScores | None): The records scores object. This exists only to keep the signature of aiobungie.crates.CharacterRecord with the record object. As it will always be None in that object.
  • **nodes (int): An int kwargs use to grab the node hashes while deserializing components.
Returns
  • aiobungie.records.Record: A standard implementation of a profile record component.
def deserialize_character_records( self, payload: collections.abc.Mapping[str, typing.Any], scores: aiobungie.crates.records.RecordScores | None = None, record_hashes: collections.abc.Sequence[int] = ()) -> aiobungie.crates.records.CharacterRecord:
479    def deserialize_character_records(
480        self,
481        payload: typedefs.JSONObject,
482        scores: records.RecordScores | None = None,
483        record_hashes: collections.Sequence[int] = (),
484    ) -> records.CharacterRecord:
485        record = self.deserialize_records(payload, scores)
486        return records.CharacterRecord(
487            scores=scores,
488            categories_node_hash=record.categories_node_hash,
489            seals_node_hash=record.seals_node_hash,
490            state=record.state,
491            objectives=record.objectives,
492            interval_objectives=record.interval_objectives,
493            redeemed_count=payload.get("intervalsRedeemedCount", 0),
494            completion_times=payload.get("completedCount"),
495            reward_visibility=payload.get("rewardVisibility"),
496            record_hashes=record_hashes,
497        )

Deserialize a JSON object of a profile character record component.

This almost does the same this as deserialize_records but has more fields which can only be found in a character record.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON object payload
Returns
  • aiobungie.records.CharacterRecord: A standard implementation of a profile character record component.
def deserialize_character_dye( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.character.Dye:
499    def deserialize_character_dye(self, payload: typedefs.JSONObject) -> character.Dye:
500        return character.Dye(
501            channel_hash=payload["channelHash"], dye_hash=payload["dyeHash"]
502        )

Deserialize a JSON payload of a character's dye information.

Parameters
Returns
  • aiobungie.crates.character.Dye: Information about a character dye object.
def deserialize_character_customization( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.character.CustomizationOptions:
504    def deserialize_character_customization(
505        self, payload: typedefs.JSONObject
506    ) -> character.CustomizationOptions:
507        return character.CustomizationOptions(
508            personality=payload["personality"],
509            face=payload["face"],
510            skin_color=payload["skinColor"],
511            lip_color=payload["lipColor"],
512            eye_color=payload["eyeColor"],
513            hair_colors=payload.get("hairColors", ()),
514            feature_colors=payload.get("featureColors", ()),
515            decal_color=payload["decalColor"],
516            wear_helmet=payload["wearHelmet"],
517            hair_index=payload["hairIndex"],
518            feature_index=payload["featureIndex"],
519            decal_index=payload["decalIndex"],
520        )

Deserialize a JSON payload of a character customization information found in character render data profile component.

Parameters
Returns
  • aiobungie.crates.character.CustomizationOptions: Information about a character customs object.
def deserialize_character_minimal_equipments( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.character.MinimalEquipments:
522    def deserialize_character_minimal_equipments(
523        self, payload: typedefs.JSONObject
524    ) -> character.MinimalEquipments:
525        if raw_dyes := payload.get("dyes"):
526            dyes = tuple(self.deserialize_character_dye(dye) for dye in raw_dyes)
527        else:
528            dyes = ()
529
530        return character.MinimalEquipments(
531            net=self._net, item_hash=payload["itemHash"], dyes=dyes
532        )

Deserialize a singular JSON peer view of equipment found in character render data profile component.

Parameters
Returns
  • aiobungie.crates.character.MinimalEquipments: A minimal equipment object.
def deserialize_character_render_data( self, payload: collections.abc.Mapping[str, typing.Any], /) -> aiobungie.crates.character.RenderedData:
534    def deserialize_character_render_data(
535        self, payload: typedefs.JSONObject, /
536    ) -> character.RenderedData:
537        return character.RenderedData(
538            net=self._net,
539            customization=self.deserialize_character_customization(
540                payload["customization"]
541            ),
542            custom_dyes=tuple(
543                self.deserialize_character_dye(dye)
544                for dye in payload["customDyes"]
545                if dye
546            ),
547            equipment=tuple(
548                self.deserialize_character_minimal_equipments(equipment)
549                for equipment in payload["peerView"]["equipment"]
550            ),
551        )

Deserialize a JSON payload of a profile character render data component.

Parameters
Returns
def deserialize_available_activity( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.activity.AvailableActivity:
553    def deserialize_available_activity(
554        self, payload: typedefs.JSONObject
555    ) -> activity.AvailableActivity:
556        return activity.AvailableActivity(
557            hash=payload["activityHash"],
558            is_new=payload["isNew"],
559            is_completed=payload["isCompleted"],
560            is_visible=payload["isVisible"],
561            display_level=payload.get("displayLevel"),
562            recommended_light=payload.get("recommendedLight"),
563            difficulty=activity.Difficulty(payload["difficultyTier"]),
564            can_join=payload["canJoin"],
565            can_lead=payload["canLead"],
566        )

Deserialize a JSON payload of an available activities.

This method is used to deserialize an array of aiobungie.crates.CharacterActivity.available_activities.

Parameters
Returns
def deserialize_character_activity( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.activity.CharacterActivity:
568    def deserialize_character_activity(
569        self, payload: typedefs.JSONObject
570    ) -> activity.CharacterActivity:
571        current_mode: enums.GameMode | None = None
572        if raw_current_mode := payload.get("currentActivityModeType"):
573            current_mode = enums.GameMode(raw_current_mode)
574
575        if raw_current_modes := payload.get("currentActivityModeTypes"):
576            current_mode_types = tuple(
577                enums.GameMode(type_) for type_ in raw_current_modes
578            )
579        else:
580            current_mode_types = ()
581
582        return activity.CharacterActivity(
583            date_started=time.clean_date(payload["dateActivityStarted"]),
584            current_hash=payload["currentActivityHash"],
585            current_mode_hash=payload["currentActivityModeHash"],
586            current_mode=current_mode,
587            current_mode_hashes=payload.get("currentActivityModeHashes", ()),
588            current_mode_types=current_mode_types,
589            current_playlist_hash=payload.get("currentPlaylistActivityHash"),
590            last_story_hash=payload["lastCompletedStoryHash"],
591            available_activities=tuple(
592                self.deserialize_available_activity(activity_)
593                for activity_ in payload["availableActivities"]
594            ),
595        )

Deserialize a JSON payload of character activity profile component.

Parameters
Returns
def deserialize_profile_items( self, payload: collections.abc.Mapping[str, typing.Any], /) -> collections.abc.Sequence[aiobungie.crates.profile.ProfileItemImpl]:
597    def deserialize_profile_items(
598        self, payload: typedefs.JSONObject, /
599    ) -> collections.Sequence[profile.ProfileItemImpl]:
600        return tuple(self.deserialize_profile_item(item) for item in payload["items"])

Deserialize a JSON payload of profile items component information.

This may deserialize profileInventories or profileCurrencies or any other alternatives.

Parameters
Returns
def deserialize_progressions( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.progressions.Progression:
643    def deserialize_progressions(
644        self, payload: typedefs.JSONObject
645    ) -> progressions.Progression:
646        return progressions.Progression(
647            hash=int(payload["progressionHash"]),
648            level=int(payload["level"]),
649            cap=int(payload["levelCap"]),
650            daily_limit=int(payload["dailyLimit"]),
651            weekly_limit=int(payload["weeklyLimit"]),
652            current_progress=int(payload["currentProgress"]),
653            daily_progress=int(payload["dailyProgress"]),
654            needed=int(payload["progressToNextLevel"]),
655            next_level=int(payload["nextLevelAt"]),
656        )
def deserialize_milestone( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.milestones.Milestone:
741    def deserialize_milestone(
742        self, payload: typedefs.JSONObject
743    ) -> milestones.Milestone:
744        start_date: datetime.datetime | None = None
745        if raw_start_date := payload.get("startDate"):
746            start_date = time.clean_date(raw_start_date)
747
748        end_date: datetime.datetime | None = None
749        if raw_end_date := payload.get("endDate"):
750            end_date = time.clean_date(raw_end_date)
751
752        rewards: collections.Collection[milestones.MilestoneReward] | None = None
753        if raw_rewards := payload.get("rewards"):
754            rewards = tuple(
755                self._deserialize_milestone_rewards(reward) for reward in raw_rewards
756            )
757
758        activities: collections.Sequence[milestones.MilestoneActivity] | None = None
759        if raw_activities := payload.get("activities"):
760            activities = tuple(
761                self._deserialize_milestone_activity(active)
762                for active in raw_activities
763            )
764
765        quests: collections.Sequence[milestones.MilestoneQuest] | None = None
766        if raw_quests := payload.get("availableQuests"):
767            quests = tuple(
768                self._deserialize_milestone_available_quest(quest)
769                for quest in raw_quests
770            )
771
772        vendors: collections.Sequence[milestones.MilestoneVendor] | None = None
773        if raw_vendors := payload.get("vendors"):
774            vendors = tuple(
775                milestones.MilestoneVendor(
776                    vendor_hash=vendor["vendorHash"],
777                    preview_itemhash=vendor.get("previewItemHash"),
778                )
779                for vendor in raw_vendors
780            )
781
782        return milestones.Milestone(
783            hash=payload["milestoneHash"],
784            start_date=start_date,
785            end_date=end_date,
786            order=payload["order"],
787            rewards=rewards,
788            available_quests=quests,
789            activities=activities,
790            vendors=vendors,
791        )
def deserialize_characters( self, payload: collections.abc.Mapping[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.character.Character]:
808    def deserialize_characters(
809        self, payload: typedefs.JSONObject
810    ) -> collections.Mapping[int, character.Character]:
811        return {
812            int(char_id): self._set_character_attrs(char)
813            for char_id, char in payload["data"].items()
814        }
def deserialize_character( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.character.Character:
816    def deserialize_character(
817        self, payload: typedefs.JSONObject
818    ) -> character.Character:
819        return self._set_character_attrs(payload)
def deserialize_character_equipments( self, payload: collections.abc.Mapping[str, typing.Any]) -> collections.abc.Mapping[int, collections.abc.Sequence[aiobungie.crates.profile.ProfileItemImpl]]:
821    def deserialize_character_equipments(
822        self, payload: typedefs.JSONObject
823    ) -> collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]:
824        return {
825            int(char_id): self.deserialize_profile_items(item)
826            for char_id, item in payload["data"].items()
827        }
def deserialize_character_activities( self, payload: collections.abc.Mapping[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.activity.CharacterActivity]:
829    def deserialize_character_activities(
830        self, payload: typedefs.JSONObject
831    ) -> collections.Mapping[int, activity.CharacterActivity]:
832        return {
833            int(char_id): self.deserialize_character_activity(data)
834            for char_id, data in payload["data"].items()
835        }
def deserialize_characters_render_data( self, payload: collections.abc.Mapping[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.character.RenderedData]:
837    def deserialize_characters_render_data(
838        self, payload: typedefs.JSONObject
839    ) -> collections.Mapping[int, character.RenderedData]:
840        return {
841            int(char_id): self.deserialize_character_render_data(data)
842            for char_id, data in payload["data"].items()
843        }
def deserialize_character_progressions( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.character.CharacterProgression:
845    def deserialize_character_progressions(
846        self, payload: typedefs.JSONObject
847    ) -> character.CharacterProgression:
848        progressions_ = {
849            int(prog_id): self.deserialize_progressions(prog)
850            for prog_id, prog in payload["progressions"].items()
851        }
852
853        factions = {
854            int(faction_id): self._deserialize_factions(faction)
855            for faction_id, faction in payload["factions"].items()
856        }
857
858        milestones_ = {
859            int(milestone_hash): self.deserialize_milestone(milestone)
860            for milestone_hash, milestone in payload["milestones"].items()
861        }
862
863        uninstanced_item_objectives = {
864            int(item_hash): [self.deserialize_objectives(ins) for ins in obj]
865            for item_hash, obj in payload["uninstancedItemObjectives"].items()
866        }
867
868        artifact = payload["seasonalArtifact"]
869        seasonal_artifact = season.CharacterScopedArtifact(
870            hash=artifact["artifactHash"],
871            points_used=artifact["pointsUsed"],
872            reset_count=artifact["resetCount"],
873            tiers=tuple(
874                self._deserialize_artifact_tiers(tier) for tier in artifact["tiers"]
875            ),
876        )
877        checklists = payload["checklists"]
878
879        return character.CharacterProgression(
880            progressions=progressions_,
881            factions=factions,
882            checklists=checklists,
883            milestones=milestones_,
884            seasonal_artifact=seasonal_artifact,
885            uninstanced_item_objectives=uninstanced_item_objectives,
886        )
def deserialize_character_progressions_mapping( self, payload: collections.abc.Mapping[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.character.CharacterProgression]:
889    def deserialize_character_progressions_mapping(self, payload: typedefs.JSONObject) -> collections.Mapping[int, character.CharacterProgression]:
890        character_progressions: collections.MutableMapping[int, character.CharacterProgression] = {}
891        for char_id, data in payload["data"].items():
892            character_progressions[int(char_id)] = self.deserialize_character_progressions(data)
893        return character_progressions
def deserialize_characters_records( self, payload: collections.abc.Mapping[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.records.CharacterRecord]:
896    def deserialize_characters_records(
897        self,
898        payload: typedefs.JSONObject,
899    ) -> collections.Mapping[int, records.CharacterRecord]:
900        return {
901            int(rec_id): self.deserialize_character_records(
902                rec, record_hashes=payload.get("featuredRecordHashes", ())
903            )
904            for rec_id, rec in payload["records"].items()
905        }
def deserialize_profile_records( self, payload: collections.abc.Mapping[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.records.Record]:
907    def deserialize_profile_records(
908        self, payload: typedefs.JSONObject
909    ) -> collections.Mapping[int, records.Record]:
910        raw_profile_records = payload["data"]
911        scores = records.RecordScores(
912            current_score=raw_profile_records["score"],
913            legacy_score=raw_profile_records["legacyScore"],
914            lifetime_score=raw_profile_records["lifetimeScore"],
915        )
916        return {
917            int(record_id): self.deserialize_records(
918                record,
919                scores,
920                categories_hash=raw_profile_records["recordCategoriesRootNodeHash"],
921                seals_hash=raw_profile_records["recordSealsRootNodeHash"],
922            )
923            for record_id, record in raw_profile_records["records"].items()
924        }
def deserialize_craftables_component( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.components.CraftablesComponent:
960    def deserialize_craftables_component(
961        self, payload: typedefs.JSONObject
962    ) -> components.CraftablesComponent:
963        return components.CraftablesComponent(
964            net=self._net,
965            craftables={
966                int(item_id): self._deserialize_craftable_item(item)
967                for item_id, item in payload["craftables"].items()
968                if item is not None
969            },
970            crafting_root_node_hash=payload["craftingRootNodeHash"],
971        )
def deserialize_components( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.components.Component:
 973    def deserialize_components(  # noqa: C901 Too complex.
 974        self, payload: typedefs.JSONObject
 975    ) -> components.Component:
 976        # Due to how complex this method is, We'll stick to
 977        # typing.Optional here.
 978
 979        profile_: profile.Profile | None = None
 980        if raw_profile := payload.get("profile"):
 981            profile_ = self.deserialize_profile(raw_profile)
 982
 983        profile_progression: profile.ProfileProgression | None = None
 984        if raw_profile_progression := payload.get("profileProgression"):
 985            profile_progression = self.deserialize_profile_progression(
 986                raw_profile_progression
 987            )
 988
 989        profile_currencies: typing.Optional[
 990            collections.Sequence[profile.ProfileItemImpl]
 991        ] = None
 992        if raw_profile_currencies := payload.get("profileCurrencies"):
 993            if "data" in raw_profile_currencies:
 994                profile_currencies = self.deserialize_profile_items(
 995                    raw_profile_currencies["data"]
 996                )
 997
 998        profile_inventories: typing.Optional[
 999            collections.Sequence[profile.ProfileItemImpl]
1000        ] = None
1001        if raw_profile_inventories := payload.get("profileInventory"):
1002            if "data" in raw_profile_inventories:
1003                profile_inventories = self.deserialize_profile_items(
1004                    raw_profile_inventories["data"]
1005                )
1006
1007        profile_records: typing.Optional[
1008            collections.Mapping[int, records.Record]
1009        ] = None
1010
1011        if raw_profile_records_ := payload.get("profileRecords"):
1012            profile_records = self.deserialize_profile_records(raw_profile_records_)
1013
1014        characters: typing.Optional[
1015            collections.Mapping[int, character.Character]
1016        ] = None
1017        if raw_characters := payload.get("characters"):
1018            characters = self.deserialize_characters(raw_characters)
1019
1020        character_records: typing.Optional[
1021            collections.Mapping[int, records.CharacterRecord]
1022        ] = None
1023
1024        if raw_character_records := payload.get("characterRecords"):
1025            # Had to do it in two steps..
1026            to_update = {}
1027            for _, data in raw_character_records["data"].items():
1028                for record_id, record in data.items():
1029                    to_update[record_id] = record
1030
1031            character_records = {
1032                int(rec_id): self.deserialize_character_records(
1033                    rec, record_hashes=to_update.get("featuredRecordHashes", ())
1034                )
1035                for rec_id, rec in to_update["records"].items()
1036            }
1037
1038        character_equipments: typing.Optional[
1039            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1040        ] = None
1041        if raw_character_equips := payload.get("characterEquipment"):
1042            character_equipments = self.deserialize_character_equipments(
1043                raw_character_equips
1044            )
1045
1046        character_inventories: typing.Optional[
1047            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1048        ] = None
1049        if raw_character_inventories := payload.get("characterInventories"):
1050            if "data" in raw_character_inventories:
1051                character_inventories = self.deserialize_character_equipments(
1052                    raw_character_inventories
1053                )
1054
1055        character_activities: typing.Optional[
1056            collections.Mapping[int, activity.CharacterActivity]
1057        ] = None
1058        if raw_char_acts := payload.get("characterActivities"):
1059            character_activities = self.deserialize_character_activities(raw_char_acts)
1060
1061        character_render_data: typing.Optional[
1062            collections.Mapping[int, character.RenderedData]
1063        ] = None
1064        if raw_character_render_data := payload.get("characterRenderData"):
1065            character_render_data = self.deserialize_characters_render_data(
1066                raw_character_render_data
1067            )
1068
1069        character_progressions: typing.Optional[
1070            collections.Mapping[int, character.CharacterProgression]
1071        ] = None
1072
1073        if raw_character_progressions := payload.get("characterProgressions"):
1074            character_progressions = self.deserialize_character_progressions_mapping(
1075                raw_character_progressions
1076            )
1077
1078        profile_string_vars: typing.Optional[collections.Mapping[int, int]] = None
1079        if raw_profile_string_vars := payload.get("profileStringVariables"):
1080            profile_string_vars = raw_profile_string_vars["data"]["integerValuesByHash"]
1081
1082        character_string_vars: typing.Optional[
1083            collections.Mapping[int, collections.Mapping[int, int]]
1084        ] = None
1085        if raw_character_string_vars := payload.get("characterStringVariables"):
1086            character_string_vars = {
1087                int(char_id): data["integerValuesByHash"]
1088                for char_id, data in raw_character_string_vars["data"].items()
1089            }
1090
1091        metrics: typing.Optional[
1092            collections.Sequence[
1093                collections.Mapping[int, tuple[bool, records.Objective | None]]
1094            ]
1095        ] = None
1096        root_node_hash: int | None = None
1097
1098        if raw_metrics := payload.get("metrics"):
1099            root_node_hash = raw_metrics["data"]["metricsRootNodeHash"]
1100            metrics = tuple(
1101                {
1102                    int(metrics_hash): (
1103                        data["invisible"],
1104                        self.deserialize_objectives(data["objectiveProgress"])
1105                        if "objectiveProgress" in data
1106                        else None,
1107                    )
1108                }
1109                for metrics_hash, data in raw_metrics["data"]["metrics"].items()
1110            )
1111        transitory: fireteams.FireteamParty | None = None
1112        if raw_transitory := payload.get("profileTransitoryData"):
1113            if "data" in raw_transitory:
1114                transitory = self.deserialize_fireteam_party(raw_transitory["data"])
1115
1116        item_components: components.ItemsComponent | None = None
1117        if raw_item_components := payload.get("itemComponents"):
1118            item_components = self.deserialize_items_component(raw_item_components)
1119
1120        profile_plugsets: typing.Optional[
1121            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1122        ] = None
1123
1124        if raw_profile_plugs := payload.get("profilePlugSets"):
1125            profile_plugsets = {
1126                int(index): [self.deserialize_plug_item_state(state) for state in data]
1127                for index, data in raw_profile_plugs["data"]["plugs"].items()
1128            }
1129
1130        character_plugsets: typing.Optional[
1131            collections.Mapping[
1132                int, collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1133            ]
1134        ] = None
1135        if raw_char_plugsets := payload.get("characterPlugSets"):
1136            character_plugsets = {
1137                int(char_id): {
1138                    int(index): [
1139                        self.deserialize_plug_item_state(state) for state in data
1140                    ]
1141                    for index, data in inner["plugs"].items()
1142                }
1143                for char_id, inner in raw_char_plugsets["data"].items()
1144            }
1145
1146        character_collectibles: typing.Optional[
1147            collections.Mapping[int, items.Collectible]
1148        ] = None
1149        if raw_character_collectibles := payload.get("characterCollectibles"):
1150            character_collectibles = {
1151                int(char_id): self._deserialize_collectible(data)
1152                for char_id, data in raw_character_collectibles["data"].items()
1153            }
1154
1155        profile_collectibles: items.Collectible | None = None
1156        if raw_profile_collectibles := payload.get("profileCollectibles"):
1157            profile_collectibles = self._deserialize_collectible(
1158                raw_profile_collectibles["data"]
1159            )
1160
1161        profile_nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1162        if raw_profile_nodes := payload.get("profilePresentationNodes"):
1163            profile_nodes = {
1164                int(node_hash): self._deserialize_node(node)
1165                for node_hash, node in raw_profile_nodes["data"]["nodes"].items()
1166            }
1167
1168        character_nodes: typing.Optional[
1169            collections.Mapping[int, collections.Mapping[int, records.Node]]
1170        ] = None
1171        if raw_character_nodes := payload.get("characterPresentationNodes"):
1172            character_nodes = {
1173                int(char_id): {
1174                    int(node_hash): self._deserialize_node(node)
1175                    for node_hash, node in each_character["nodes"].items()
1176                }
1177                for char_id, each_character in raw_character_nodes["data"].items()
1178            }
1179
1180        platform_silver: typing.Optional[
1181            collections.Mapping[str, profile.ProfileItemImpl]
1182        ] = None
1183        if raw_platform_silver := payload.get("platformSilver"):
1184            if "data" in raw_platform_silver:
1185                platform_silver = {
1186                    platform_name: self.deserialize_profile_item(item)
1187                    for platform_name, item in raw_platform_silver["data"][
1188                        "platformSilver"
1189                    ].items()
1190                }
1191
1192        character_currency_lookups: typing.Optional[
1193            collections.Mapping[int, collections.Sequence[items.Currency]]
1194        ] = None
1195        if raw_char_lookups := payload.get("characterCurrencyLookups"):
1196            if "data" in raw_char_lookups:
1197                character_currency_lookups = {
1198                    int(char_id): self._deserialize_currencies(currency)
1199                    for char_id, currency in raw_char_lookups["data"].items()
1200                }
1201
1202        character_craftables: typing.Optional[
1203            collections.Mapping[int, components.CraftablesComponent]
1204        ] = None
1205        if raw_character_craftables := payload.get("characterCraftables"):
1206            if "data" in raw_character_craftables:
1207                character_craftables = {
1208                    int(char_id): self.deserialize_craftables_component(craftable)
1209                    for char_id, craftable in raw_character_craftables["data"].items()
1210                }
1211
1212        return components.Component(
1213            profiles=profile_,
1214            profile_progression=profile_progression,
1215            profile_currencies=profile_currencies,
1216            profile_inventories=profile_inventories,
1217            profile_records=profile_records,
1218            characters=characters,
1219            character_records=character_records,
1220            character_equipments=character_equipments,
1221            character_inventories=character_inventories,
1222            character_activities=character_activities,
1223            character_render_data=character_render_data,
1224            character_progressions=character_progressions,
1225            profile_string_variables=profile_string_vars,
1226            character_string_variables=character_string_vars,
1227            metrics=metrics,
1228            root_node_hash=root_node_hash,
1229            transitory=transitory,
1230            item_components=item_components,
1231            profile_plugsets=profile_plugsets,
1232            character_plugsets=character_plugsets,
1233            character_collectibles=character_collectibles,
1234            profile_collectibles=profile_collectibles,
1235            profile_nodes=profile_nodes,
1236            character_nodes=character_nodes,
1237            platform_silver=platform_silver,
1238            character_currency_lookups=character_currency_lookups,
1239            character_craftables=character_craftables,
1240        )

Deserialize a JSON payload of Bungie.net profile components information.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_items_component( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.components.ItemsComponent:
1242    def deserialize_items_component(
1243        self, payload: typedefs.JSONObject
1244    ) -> components.ItemsComponent:
1245        # Due to how complex this method is, We'll stick to typing.Optional.
1246        instances: typing.Optional[
1247            collections.Sequence[collections.Mapping[int, items.ItemInstance]]
1248        ] = None
1249        if raw_instances := payload.get("instances"):
1250            instances = tuple(
1251                {int(ins_id): self.deserialize_instanced_item(item)}
1252                for ins_id, item in raw_instances["data"].items()
1253            )
1254
1255        render_data: typing.Optional[
1256            collections.Mapping[int, tuple[bool, dict[int, int]]]
1257        ] = None
1258        if raw_render_data := payload.get("renderData"):
1259            render_data = {
1260                int(ins_id): (data["useCustomDyes"], data["artRegions"])
1261                for ins_id, data in raw_render_data["data"].items()
1262            }
1263
1264        stats: typing.Optional[collections.Mapping[int, items.ItemStatsView]] = None
1265        if raw_stats := payload.get("stats"):
1266            stats = {}
1267            for ins_id, stat in raw_stats["data"].items():
1268                for _, items_ in stat.items():
1269                    stats[int(ins_id)] = self.deserialize_item_stats_view(items_)
1270
1271        sockets: typing.Optional[
1272            collections.Mapping[int, collections.Sequence[items.ItemSocket]]
1273        ] = None
1274        if raw_sockets := payload.get("sockets"):
1275            sockets = {
1276                int(ins_id): tuple(
1277                    self.deserialize_item_socket(socket) for socket in item["sockets"]
1278                )
1279                for ins_id, item in raw_sockets["data"].items()
1280            }
1281
1282        objectives: typing.Optional[
1283            collections.Mapping[int, collections.Sequence[records.Objective]]
1284        ] = None
1285        if raw_objectives := payload.get("objectives"):
1286            objectives = {
1287                int(ins_id): tuple(
1288                    self.deserialize_objectives(objective)
1289                    for objective in data["objectives"]
1290                )
1291                for ins_id, data in raw_objectives["data"].items()
1292            }
1293
1294        perks: typing.Optional[
1295            collections.Mapping[int, collections.Collection[items.ItemPerk]]
1296        ] = None
1297        if raw_perks := payload.get("perks"):
1298            perks = {
1299                int(ins_id): tuple(
1300                    self.deserialize_item_perk(perk) for perk in item["perks"]
1301                )
1302                for ins_id, item in raw_perks["data"].items()
1303            }
1304
1305        plug_states: typing.Optional[collections.Sequence[items.PlugItemState]] = None
1306        if raw_plug_states := payload.get("plugStates"):
1307            plug_states = tuple(
1308                self.deserialize_plug_item_state(plug)
1309                for _, plug in raw_plug_states["data"].items()
1310            )
1311
1312        reusable_plugs: typing.Optional[
1313            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1314        ] = None
1315        if raw_re_plugs := payload.get("reusablePlugs"):
1316            reusable_plugs = {
1317                int(ins_id): tuple(
1318                    self.deserialize_plug_item_state(state) for state in inner
1319                )
1320                for ins_id, plug in raw_re_plugs["data"].items()
1321                for inner in tuple(plug["plugs"].values())
1322            }
1323
1324        plug_objectives: typing.Optional[
1325            collections.Mapping[
1326                int, collections.Mapping[int, collections.Collection[records.Objective]]
1327            ]
1328        ] = None
1329        if raw_plug_objectives := payload.get("plugObjectives"):
1330            plug_objectives = {
1331                int(ins_id): {
1332                    int(obj_hash): tuple(
1333                        self.deserialize_objectives(obj) for obj in objs
1334                    )
1335                    for obj_hash, objs in inner["objectivesPerPlug"].items()
1336                }
1337                for ins_id, inner in raw_plug_objectives["data"].items()
1338            }
1339
1340        return components.ItemsComponent(
1341            sockets=sockets,
1342            stats=stats,
1343            render_data=render_data,
1344            instances=instances,
1345            objectives=objectives,
1346            perks=perks,
1347            plug_states=plug_states,
1348            reusable_plugs=reusable_plugs,
1349            plug_objectives=plug_objectives,
1350        )

Deserialize a JSON objects within the itemComponents key.`

def deserialize_character_component( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.components.CharacterComponent:
1352    def deserialize_character_component(
1353        self, payload: typedefs.JSONObject
1354    ) -> components.CharacterComponent:
1355        character_: character.Character | None = None
1356        if raw_singular_character := payload.get("character"):
1357            character_ = self.deserialize_character(raw_singular_character["data"])
1358
1359        inventory: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1360        if raw_inventory := payload.get("inventory"):
1361            if "data" in raw_inventory:
1362                inventory = self.deserialize_profile_items(raw_inventory["data"])
1363
1364        activities: activity.CharacterActivity | None = None
1365        if raw_activities := payload.get("activities"):
1366            activities = self.deserialize_character_activity(raw_activities["data"])
1367
1368        equipment: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1369        if raw_equipments := payload.get("equipment"):
1370            equipment = self.deserialize_profile_items(raw_equipments["data"])
1371
1372        progressions_: character.CharacterProgression | None = None
1373        if raw_progressions := payload.get("progressions"):
1374            progressions_ = self.deserialize_character_progressions(
1375                raw_progressions["data"]
1376            )
1377
1378        render_data: character.RenderedData | None = None
1379        if raw_render_data := payload.get("renderData"):
1380            render_data = self.deserialize_character_render_data(
1381                raw_render_data["data"]
1382            )
1383
1384        character_records: typing.Optional[
1385            collections.Mapping[int, records.CharacterRecord]
1386        ] = None
1387        if raw_char_records := payload.get("records"):
1388            character_records = self.deserialize_characters_records(
1389                raw_char_records["data"]
1390            )
1391
1392        item_components: components.ItemsComponent | None = None
1393        if raw_item_components := payload.get("itemComponents"):
1394            item_components = self.deserialize_items_component(raw_item_components)
1395
1396        nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1397        if raw_nodes := payload.get("presentationNodes"):
1398            nodes = {
1399                int(node_hash): self._deserialize_node(node)
1400                for node_hash, node in raw_nodes["data"]["nodes"].items()
1401            }
1402
1403        collectibles: items.Collectible | None = None
1404        if raw_collectibles := payload.get("collectibles"):
1405            collectibles = self._deserialize_collectible(raw_collectibles["data"])
1406
1407        currency_lookups: typing.Optional[collections.Sequence[items.Currency]] = None
1408        if raw_currencies := payload.get("currencyLookups"):
1409            if "data" in raw_currencies:
1410                currency_lookups = self._deserialize_currencies(raw_currencies)
1411
1412        return components.CharacterComponent(
1413            activities=activities,
1414            equipment=equipment,
1415            inventory=inventory,
1416            progressions=progressions_,
1417            render_data=render_data,
1418            character=character_,
1419            character_records=character_records,
1420            profile_records=None,
1421            item_components=item_components,
1422            currency_lookups=currency_lookups,
1423            collectibles=collectibles,
1424            nodes=nodes,
1425        )

Deserialize a JSON payload of Destiny 2 character component.

Parameters
Returns
def deserialize_inventory_results( self, payload: collections.abc.Mapping[str, typing.Any]) -> Iterator[aiobungie.crates.entity.SearchableEntity]:
1444    def deserialize_inventory_results(
1445        self, payload: typedefs.JSONObject
1446    ) -> iterators.Iterator[entity.SearchableEntity]:
1447        return iterators.Iterator(
1448            [
1449                entity.SearchableEntity(
1450                    net=self._net,
1451                    hash=data["hash"],
1452                    entity_type=data["entityType"],
1453                    weight=data["weight"],
1454                    suggested_words=payload["suggestedWords"],
1455                    name=data["displayProperties"]["name"],
1456                    has_icon=data["displayProperties"]["hasIcon"],
1457                    description=typedefs.unknown(
1458                        data["displayProperties"]["description"]
1459                    ),
1460                    icon=assets.Image(path=data["displayProperties"]["icon"]),
1461                )
1462                for data in payload["results"]["results"]
1463            ]
1464        )

Deserialize results of searched Destiny2 entities.

Parameters
Returns
def deserialize_inventory_entity( self, payload: collections.abc.Mapping[str, typing.Any], /) -> aiobungie.crates.entity.InventoryEntity:
1493    def deserialize_inventory_entity(  # noqa: C901 Too complex.
1494        self, payload: typedefs.JSONObject, /
1495    ) -> entity.InventoryEntity:
1496        props = self._set_entity_attrs(payload)
1497        objects = self._deserialize_inventory_item_objects(payload)
1498
1499        collectible_hash: int | None = None
1500        if raw_collectible_hash := payload.get("collectibleHash"):
1501            collectible_hash = int(raw_collectible_hash)
1502
1503        secondary_icon: assets.Image | None = None
1504        if raw_second_icon := payload.get("secondaryIcon"):
1505            secondary_icon = assets.Image(path=raw_second_icon)
1506
1507        secondary_overlay: assets.Image | None = None
1508        if raw_second_overlay := payload.get("secondaryOverlay"):
1509            secondary_overlay = assets.Image(path=raw_second_overlay)
1510
1511        secondary_special: assets.Image | None = None
1512        if raw_second_special := payload.get("secondarySpecial"):
1513            secondary_special = assets.Image(path=raw_second_special)
1514
1515        screenshot: assets.Image | None = None
1516        if raw_screenshot := payload.get("screenshot"):
1517            screenshot = assets.Image(path=raw_screenshot)
1518
1519        watermark_icon: assets.Image | None = None
1520        if raw_watermark_icon := payload.get("iconWatermark"):
1521            watermark_icon = assets.Image(path=raw_watermark_icon)
1522
1523        watermark_shelved: assets.Image | None = None
1524        if raw_watermark_shelved := payload.get("iconWatermarkShelved"):
1525            watermark_shelved = assets.Image(path=raw_watermark_shelved)
1526
1527        about: str | None = None
1528        if raw_about := payload.get("flavorText"):
1529            about = raw_about
1530
1531        ui_item_style: str | None = None
1532        if raw_ui_style := payload.get("uiItemDisplayStyle"):
1533            ui_item_style = raw_ui_style
1534
1535        tier_and_name: str | None = None
1536        if raw_tier_and_name := payload.get("itemTypeAndTierDisplayName"):
1537            tier_and_name = raw_tier_and_name
1538
1539        type_name: str | None = None
1540        if raw_type_name := payload.get("itemTypeDisplayName"):
1541            type_name = raw_type_name
1542
1543        display_source: str | None = None
1544        if raw_display_source := payload.get("displaySource"):
1545            display_source = raw_display_source
1546
1547        lorehash: int | None = None
1548        if raw_lore_hash := payload.get("loreHash"):
1549            lorehash = int(raw_lore_hash)
1550
1551        summary_hash: int | None = None
1552        if raw_summary_hash := payload.get("summaryItemHash"):
1553            summary_hash = raw_summary_hash
1554
1555        breaker_type_hash: int | None = None
1556        if raw_breaker_type_hash := payload.get("breakerTypeHash"):
1557            breaker_type_hash = int(raw_breaker_type_hash)
1558
1559        damage_types: typing.Optional[collections.Sequence[int]] = None
1560        if raw_damage_types := payload.get("damageTypes"):
1561            damage_types = tuple(int(type_) for type_ in raw_damage_types)
1562
1563        damagetype_hashes: typing.Optional[collections.Sequence[int]] = None
1564        if raw_damagetype_hashes := payload.get("damageTypeHashes"):
1565            damagetype_hashes = tuple(int(type_) for type_ in raw_damagetype_hashes)
1566
1567        default_damagetype_hash: int | None = None
1568        if raw_defaultdmg_hash := payload.get("defaultDamageTypeHash"):
1569            default_damagetype_hash = int(raw_defaultdmg_hash)
1570
1571        emblem_objective_hash: int | None = None
1572        if raw_emblem_obj_hash := payload.get("emblemObjectiveHash"):
1573            emblem_objective_hash = int(raw_emblem_obj_hash)
1574
1575        tier_type: enums.TierType | None = None
1576        tier: enums.ItemTier | None = None
1577        bucket_hash: int | None = None
1578        recovery_hash: int | None = None
1579        tier_name: str | None = None
1580        isinstance_item: bool = False
1581        expire_tool_tip: str | None = None
1582        expire_in_orbit_message: str | None = None
1583        suppress_expiration: bool = False
1584        max_stack_size: int | None = None
1585        stack_label: str | None = None
1586
1587        if inventory := payload.get("inventory"):
1588            tier_type = enums.TierType(int(inventory["tierType"]))
1589            tier = enums.ItemTier(int(inventory["tierTypeHash"]))
1590            bucket_hash = int(inventory["bucketTypeHash"])
1591            recovery_hash = int(inventory["recoveryBucketTypeHash"])
1592            tier_name = inventory["tierTypeName"]
1593            isinstance_item = inventory["isInstanceItem"]
1594            suppress_expiration = inventory["suppressExpirationWhenObjectivesComplete"]
1595            max_stack_size = int(inventory["maxStackSize"])
1596
1597            try:
1598                stack_label = inventory["stackUniqueLabel"]
1599            except KeyError:
1600                pass
1601
1602        if "traitHashes" in payload:
1603            trait_hashes = tuple(
1604                int(trait_hash) for trait_hash in payload["traitHashes"]
1605            )
1606        else:
1607            trait_hashes = ()
1608
1609        if "traitIds" in payload:
1610            trait_ids = tuple(trait_id for trait_id in payload["traitIds"])
1611        else:
1612            trait_ids = ()
1613
1614        return entity.InventoryEntity(
1615            net=self._net,
1616            collectible_hash=collectible_hash,
1617            name=props.name,
1618            about=about,
1619            emblem_objective_hash=emblem_objective_hash,
1620            suppress_expiration=suppress_expiration,
1621            max_stack_size=max_stack_size,
1622            stack_label=stack_label,
1623            tier=tier,
1624            tier_type=tier_type,
1625            tier_name=tier_name,
1626            bucket_hash=bucket_hash,
1627            recovery_bucket_hash=recovery_hash,
1628            isinstance_item=isinstance_item,
1629            expire_in_orbit_message=expire_in_orbit_message,
1630            expiration_tooltip=expire_tool_tip,
1631            lore_hash=lorehash,
1632            type_and_tier_name=tier_and_name,
1633            summary_hash=summary_hash,
1634            ui_display_style=ui_item_style,
1635            type_name=type_name,
1636            breaker_type_hash=breaker_type_hash,
1637            description=props.description,
1638            display_source=display_source,
1639            hash=props.hash,
1640            damage_types=damage_types,
1641            index=props.index,
1642            icon=props.icon,
1643            has_icon=props.has_icon,
1644            screenshot=screenshot,
1645            watermark_icon=watermark_icon,
1646            watermark_shelved=watermark_shelved,
1647            secondary_icon=secondary_icon,
1648            secondary_overlay=secondary_overlay,
1649            secondary_special=secondary_special,
1650            type=enums.ItemType(int(payload["itemType"])),
1651            category_hashes=tuple(
1652                int(hash_) for hash_ in payload["itemCategoryHashes"]
1653            ),
1654            item_class=enums.Class(int(payload["classType"])),
1655            sub_type=enums.ItemSubType(int(payload["itemSubType"])),
1656            breaker_type=int(payload["breakerType"]),
1657            default_damagetype=int(payload["defaultDamageType"]),
1658            default_damagetype_hash=default_damagetype_hash,
1659            damagetype_hashes=damagetype_hashes,
1660            tooltip_notifications=payload["tooltipNotifications"],
1661            not_transferable=payload["nonTransferrable"],
1662            allow_actions=payload["allowActions"],
1663            is_equippable=payload["equippable"],
1664            objects=objects,
1665            background_colors=payload.get("backgroundColor", {}),
1666            season_hash=payload.get("seasonHash"),
1667            has_postmaster_effect=payload["doesPostmasterPullHaveSideEffects"],
1668            trait_hashes=trait_hashes,
1669            trait_ids=trait_ids,
1670        )

Deserialize a JSON payload of an inventory entity item information.

This can be any item from DestinyInventoryItemDefinition definition.

Parameters
Returns
def deserialize_objective_entity( self, payload: collections.abc.Mapping[str, typing.Any], /) -> aiobungie.crates.entity.ObjectiveEntity:
1672    def deserialize_objective_entity(
1673        self, payload: typedefs.JSONObject, /
1674    ) -> entity.ObjectiveEntity:
1675        props = self._set_entity_attrs(payload)
1676        return entity.ObjectiveEntity(
1677            net=self._net,
1678            hash=props.hash,
1679            index=props.index,
1680            description=props.description,
1681            name=props.name,
1682            has_icon=props.has_icon,
1683            icon=props.icon,
1684            unlock_value_hash=payload["unlockValueHash"],
1685            completion_value=payload["completionValue"],
1686            scope=entity.GatingScope(int(payload["scope"])),
1687            location_hash=payload["locationHash"],
1688            allowed_negative_value=payload["allowNegativeValue"],
1689            allowed_value_change=payload["allowValueChangeWhenCompleted"],
1690            counting_downward=payload["isCountingDownward"],
1691            value_style=entity.ValueUIStyle(int(payload["valueStyle"])),
1692            progress_description=payload["progressDescription"],
1693            perks=payload["perks"],
1694            stats=payload["stats"],
1695            minimum_visibility=payload["minimumVisibilityThreshold"],
1696            allow_over_completion=payload["allowOvercompletion"],
1697            show_value_style=payload["showValueOnComplete"],
1698            display_only_objective=payload["isDisplayOnlyObjective"],
1699            complete_value_style=entity.ValueUIStyle(
1700                int(payload["completedValueStyle"])
1701            ),
1702            progress_value_style=entity.ValueUIStyle(
1703                int(payload["inProgressValueStyle"])
1704            ),
1705            ui_label=payload["uiLabel"],
1706            ui_style=entity.ObjectiveUIStyle(int(payload["uiStyle"])),
1707        )

Deserialize a JSON payload of an objective entity information.

Parameters
Returns
def deserialize_activity( self, payload: collections.abc.Mapping[str, typing.Any], /) -> aiobungie.crates.activity.Activity:
1735    def deserialize_activity(
1736        self,
1737        payload: typedefs.JSONObject,
1738        /,
1739    ) -> activity.Activity:
1740        period = time.clean_date(payload["period"])
1741        details = payload["activityDetails"]
1742        ref_id = int(details["referenceId"])
1743        instance_id = int(details["instanceId"])
1744        mode = enums.GameMode(details["mode"])
1745        modes = tuple(enums.GameMode(int(mode_)) for mode_ in details["modes"])
1746        is_private = details["isPrivate"]
1747        membership_type = enums.MembershipType(int(details["membershipType"]))
1748
1749        # Since we're using the same fields for post activity method
1750        # this check is required since post activity doesn't values values
1751        values = self._deserialize_activity_values(payload["values"])
1752
1753        return activity.Activity(
1754            net=self._net,
1755            hash=ref_id,
1756            instance_id=instance_id,
1757            mode=mode,
1758            modes=modes,
1759            is_private=is_private,
1760            membership_type=membership_type,
1761            occurred_at=period,
1762            values=values,
1763        )

Deserialize a JSON payload of an activity history information.

Parameters
Returns
def deserialize_activities( self, payload: collections.abc.Mapping[str, typing.Any]) -> Iterator[aiobungie.crates.activity.Activity]:
1765    def deserialize_activities(
1766        self, payload: typedefs.JSONObject
1767    ) -> iterators.Iterator[activity.Activity]:
1768        return iterators.Iterator(
1769            [
1770                self.deserialize_activity(activity_)
1771                for activity_ in payload["activities"]
1772            ]
1773        )

Deserialize a JSON payload of an array of activity history information.

Parameters
Returns
def deserialize_extended_weapon_values( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.activity.ExtendedWeaponValues:
1775    def deserialize_extended_weapon_values(
1776        self, payload: typedefs.JSONObject
1777    ) -> activity.ExtendedWeaponValues:
1778        assists: int | None = None
1779        if raw_assists := payload["values"].get("uniqueWeaponAssists"):
1780            assists = raw_assists["basic"]["value"]
1781        assists_damage: int | None = None
1782
1783        if raw_assists_damage := payload["values"].get("uniqueWeaponAssistDamage"):
1784            assists_damage = raw_assists_damage["basic"]["value"]
1785
1786        return activity.ExtendedWeaponValues(
1787            reference_id=int(payload["referenceId"]),
1788            kills=payload["values"]["uniqueWeaponKills"]["basic"]["value"],
1789            precision_kills=payload["values"]["uniqueWeaponPrecisionKills"]["basic"][
1790                "value"
1791            ],
1792            assists=assists,
1793            assists_damage=assists_damage,
1794            precision_kills_percentage=(
1795                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"]["value"],
1796                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"][
1797                    "displayValue"
1798                ],
1799            ),
1800        )

Deserialize values of extended weapons JSON object.

Parameters
Returns
def deserialize_post_activity_player( self, payload: collections.abc.Mapping[str, typing.Any], /) -> aiobungie.crates.activity.PostActivityPlayer:
1821    def deserialize_post_activity_player(
1822        self, payload: typedefs.JSONObject, /
1823    ) -> activity.PostActivityPlayer:
1824        player = payload["player"]
1825
1826        class_hash: int | None = None
1827        if (class_hash := player.get("classHash")) is not None:
1828            class_hash = class_hash
1829
1830        race_hash: int | None = None
1831        if (race_hash := player.get("raceHash")) is not None:
1832            race_hash = race_hash
1833
1834        gender_hash: int | None = None
1835        if (gender_hash := player.get("genderHash")) is not None:
1836            gender_hash = gender_hash
1837
1838        character_class: str | None = None
1839        if character_class := player.get("characterClass"):
1840            character_class = character_class
1841
1842        character_level: int | None = None
1843        if (character_level := player.get("characterLevel")) is not None:
1844            character_level = character_level
1845
1846        return activity.PostActivityPlayer(
1847            standing=int(payload["standing"]),
1848            score=int(payload["score"]["basic"]["value"]),
1849            character_id=payload["characterId"],
1850            destiny_user=self.deserialize_destiny_membership(player["destinyUserInfo"]),
1851            character_class=character_class,
1852            character_level=character_level,
1853            race_hash=race_hash,
1854            gender_hash=gender_hash,
1855            class_hash=class_hash,
1856            light_level=int(player["lightLevel"]),
1857            emblem_hash=int(player["emblemHash"]),
1858            values=self._deserialize_activity_values(payload["values"]),
1859            extended_values=self._deserialize_extended_values(payload["extended"]),
1860        )

Deserialize a JSON payload of a post activity player information.

Parameters
Returns
def deserialize_post_activity( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.activity.PostActivity:
1872    def deserialize_post_activity(
1873        self, payload: typedefs.JSONObject
1874    ) -> activity.PostActivity:
1875        period = time.clean_date(payload["period"])
1876        details = payload["activityDetails"]
1877        ref_id = int(details["referenceId"])
1878        instance_id = int(details["instanceId"])
1879        mode = enums.GameMode(details["mode"])
1880        modes = tuple(enums.GameMode(int(mode_)) for mode_ in details["modes"])
1881        is_private = details["isPrivate"]
1882        membership_type = enums.MembershipType(int(details["membershipType"]))
1883        return activity.PostActivity(
1884            net=self._net,
1885            hash=ref_id,
1886            membership_type=membership_type,
1887            instance_id=instance_id,
1888            mode=mode,
1889            modes=modes,
1890            is_private=is_private,
1891            occurred_at=period,
1892            starting_phase=int(payload["startingPhaseIndex"]),
1893            players=tuple(
1894                self.deserialize_post_activity_player(player)
1895                for player in payload["entries"]
1896            ),
1897            teams=tuple(
1898                self._deserialize_post_activity_team(team) for team in payload["teams"]
1899            ),
1900        )

Deserialize a JSON payload of a post activity information.

Parameters
Returns
def deserialize_aggregated_activity( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.activity.AggregatedActivity:
1938    def deserialize_aggregated_activity(
1939        self, payload: typedefs.JSONObject
1940    ) -> activity.AggregatedActivity:
1941        return activity.AggregatedActivity(
1942            hash=int(payload["activityHash"]),
1943            values=self._deserialize_aggregated_activity_values(payload["values"]),
1944        )

Deserialize a JSON payload of an aggregated activity.

Parameters
Returns
def deserialize_aggregated_activities( self, payload: collections.abc.Mapping[str, typing.Any]) -> Iterator[aiobungie.crates.activity.AggregatedActivity]:
1946    def deserialize_aggregated_activities(
1947        self, payload: typedefs.JSONObject
1948    ) -> iterators.Iterator[activity.AggregatedActivity]:
1949        return iterators.Iterator(
1950            [
1951                self.deserialize_aggregated_activity(activity)
1952                for activity in payload["activities"]
1953            ]
1954        )

Deserialize a JSON payload of an array of aggregated activities.

Parameters
Returns
def deserialize_linked_profiles( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.profile.LinkedProfile:
1956    def deserialize_linked_profiles(
1957        self, payload: typedefs.JSONObject
1958    ) -> profile.LinkedProfile:
1959        bungie_user = self.deserialize_partial_bungie_user(payload["bnetMembership"])
1960
1961        if raw_profile := payload.get("profiles"):
1962            profiles = tuple(
1963                self.deserialize_destiny_membership(p) for p in raw_profile
1964            )
1965        else:
1966            profiles = ()
1967
1968        error_profiles = ()
1969        if raw_profiles_with_errors := payload.get("profilesWithErrors"):
1970            for raw_error_p in raw_profiles_with_errors:
1971                if "infoCard" in raw_error_p:
1972                    error_profiles = tuple(
1973                        self.deserialize_destiny_membership(error_p)
1974                        for error_p in raw_error_p
1975                    )
1976
1977        return profile.LinkedProfile(
1978            bungie_user=bungie_user,
1979            profiles=profiles,
1980            profiles_with_errors=error_profiles,
1981        )

Deserialize a JSON payload of Bungie.net hard linked profile information.

Parameters
Returns
def deserialize_clan_banners( self, payload: collections.abc.Mapping[str, typing.Any]) -> collections.abc.Sequence[aiobungie.crates.clans.ClanBanner]:
1983    def deserialize_clan_banners(
1984        self, payload: typedefs.JSONObject
1985    ) -> collections.Sequence[clans.ClanBanner]:
1986        if banners := payload.get("clanBannerDecals"):
1987            banner_obj = tuple(
1988                clans.ClanBanner(
1989                    id=int(k),
1990                    foreground=assets.Image(path=v["foregroundPath"]),
1991                    background=assets.Image(path=v["backgroundPath"]),
1992                )
1993                for k, v in banners.items()
1994            )
1995        else:
1996            banner_obj = ()
1997        return banner_obj

Deserialize a JSON array of a clan banners information.

Parameters
Returns
def deserialize_public_milestone_content( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.milestones.MilestoneContent:
1999    def deserialize_public_milestone_content(
2000        self, payload: typedefs.JSONObject
2001    ) -> milestones.MilestoneContent:
2002        if raw_categories := payload.get("itemCategories"):
2003            items_categories = tuple(
2004                milestones.MilestoneItems(
2005                    title=item["title"], hashes=item["itemHashes"]
2006                )
2007                for item in raw_categories
2008            )
2009        else:
2010            items_categories = ()
2011
2012        return milestones.MilestoneContent(
2013            about=typedefs.unknown(payload["about"]),
2014            status=typedefs.unknown(payload["status"]),
2015            tips=payload.get("tips", ()),
2016            items=items_categories,
2017        )

Deserialize a JSON payload of milestone content information.

Parameters
Returns
def deserialize_friend( self, payload: collections.abc.Mapping[str, typing.Any], /) -> aiobungie.crates.friends.Friend:
2019    def deserialize_friend(self, payload: typedefs.JSONObject, /) -> friends.Friend:
2020        bungie_user: user.BungieUser | None = None
2021
2022        if raw_bungie_user := payload.get("bungieNetUser"):
2023            bungie_user = self.deserialize_bungie_user(raw_bungie_user)
2024
2025        return friends.Friend(
2026            net=self._net,
2027            id=int(payload["lastSeenAsMembershipId"]),
2028            name=typedefs.unknown(payload["bungieGlobalDisplayName"]),
2029            code=payload.get("bungieGlobalDisplayNameCode"),
2030            relationship=enums.Relationship(payload["relationship"]),
2031            user=bungie_user,
2032            online_status=enums.Presence(payload["onlineStatus"]),
2033            online_title=payload["onlineTitle"],
2034            type=enums.MembershipType(payload["lastSeenAsBungieMembershipType"]),
2035        )

Deserialize a JSON payload of a Bungie friend information.

Parameters
Returns
def deserialize_friends( self, payload: collections.abc.Mapping[str, typing.Any]) -> collections.abc.Sequence[aiobungie.crates.friends.Friend]:
2037    def deserialize_friends(
2038        self, payload: typedefs.JSONObject
2039    ) -> collections.Sequence[friends.Friend]:
2040        return tuple(self.deserialize_friend(friend) for friend in payload["friends"])

Deserialize a JSON sequence of Bungie friends information.

This is usually used to deserialize the incoming/outgoing friend requests.

Parameters
Returns
def deserialize_friend_requests( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.friends.FriendRequestView:
2042    def deserialize_friend_requests(
2043        self, payload: typedefs.JSONObject
2044    ) -> friends.FriendRequestView:
2045        if raw_incoming_requests := payload.get("incomingRequests"):
2046            incoming = tuple(
2047                self.deserialize_friend(incoming_request)
2048                for incoming_request in raw_incoming_requests
2049            )
2050        else:
2051            incoming = ()
2052
2053        if raw_outgoing_requests := payload.get("outgoingRequests"):
2054            outgoing = tuple(
2055                self.deserialize_friend(outgoing_request)
2056                for outgoing_request in raw_outgoing_requests
2057            )
2058        else:
2059            outgoing = ()
2060
2061        return friends.FriendRequestView(incoming=incoming, outgoing=outgoing)

Deserialize a JSON sequence of Bungie friend requests information.

This is used for incoming/outgoing friend requests.

Parameters
Returns
def deserialize_fireteams( self, payload: collections.abc.Mapping[str, typing.Any]) -> collections.abc.Sequence[aiobungie.crates.fireteams.Fireteam]:
2092    def deserialize_fireteams(
2093        self, payload: typedefs.JSONObject
2094    ) -> collections.Sequence[fireteams.Fireteam]:
2095        if "results" in payload:
2096            fireteams_ = tuple(
2097                self._set_fireteam_fields(
2098                    elem, total_results=int(payload["totalResults"])
2099                )
2100                for elem in payload["results"]
2101            )
2102        else:
2103            fireteams_ = ()
2104        return fireteams_

Deserialize a JSON sequence of Bungie fireteams information.

Parameters
Returns
def deserialize_fireteam_destiny_users( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.fireteams.FireteamUser:
2106    def deserialize_fireteam_destiny_users(
2107        self, payload: typedefs.JSONObject
2108    ) -> fireteams.FireteamUser:
2109        destiny_obj = self.deserialize_destiny_membership(payload)
2110        return fireteams.FireteamUser(
2111            net=self._net,
2112            id=destiny_obj.id,
2113            code=destiny_obj.code,
2114            icon=destiny_obj.icon,
2115            types=destiny_obj.types,
2116            type=destiny_obj.type,
2117            is_public=destiny_obj.is_public,
2118            crossave_override=destiny_obj.crossave_override,
2119            name=destiny_obj.name,
2120            last_seen_name=destiny_obj.last_seen_name,
2121            fireteam_display_name=payload["FireteamDisplayName"],
2122            fireteam_membership_id=enums.MembershipType(
2123                payload["FireteamMembershipType"]
2124            ),
2125        )

Deserialize a JSON payload of Bungie fireteam destiny users information.

Parameters
Returns
def deserialize_fireteam_members( self, payload: collections.abc.Mapping[str, typing.Any], *, alternatives: bool = False) -> collections.abc.Sequence[aiobungie.crates.fireteams.FireteamMember]:
2127    def deserialize_fireteam_members(
2128        self, payload: typedefs.JSONObject, *, alternatives: bool = False
2129    ) -> collections.Sequence[fireteams.FireteamMember]:
2130        members_: list[fireteams.FireteamMember] = []
2131        if members := payload.get("Members" if not alternatives else "Alternates"):
2132            for member in members:
2133                bungie_fields = self.deserialize_partial_bungie_user(member)
2134                members_fields = fireteams.FireteamMember(
2135                    destiny_user=self.deserialize_fireteam_destiny_users(member),
2136                    has_microphone=member["hasMicrophone"],
2137                    character_id=int(member["characterId"]),
2138                    date_joined=time.clean_date(member["dateJoined"]),
2139                    last_platform_invite_date=time.clean_date(
2140                        member["lastPlatformInviteAttemptDate"]
2141                    ),
2142                    last_platform_invite_result=int(
2143                        member["lastPlatformInviteAttemptResult"]
2144                    ),
2145                    net=self._net,
2146                    name=bungie_fields.name,
2147                    id=bungie_fields.id,
2148                    icon=bungie_fields.icon,
2149                    is_public=bungie_fields.is_public,
2150                    crossave_override=bungie_fields.crossave_override,
2151                    types=bungie_fields.types,
2152                    type=bungie_fields.type,
2153                )
2154                members_.append(members_fields)
2155        return tuple(members_)

Deserialize a JSON sequence of Bungie fireteam members information.

Parameters
  • payload (aiobungie.typedefs.JSONObject): The JSON payload.
  • alternatives (bool): If set to True, Then it will deserialize the alternatives data in the payload. If not the it will just deserialize the members data.
Returns
def deserialize_available_fireteam( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.fireteams.AvailableFireteam:
2157    def deserialize_available_fireteam(
2158        self, payload: typedefs.JSONObject
2159    ) -> fireteams.AvailableFireteam:
2160        fields = self._set_fireteam_fields(payload["Summary"])
2161        return fireteams.AvailableFireteam(
2162            id=fields.id,
2163            group_id=fields.group_id,
2164            platform=fields.platform,
2165            activity_type=fields.activity_type,
2166            is_immediate=fields.is_immediate,
2167            is_public=fields.is_public,
2168            is_valid=fields.is_valid,
2169            owner_id=fields.owner_id,
2170            player_slot_count=fields.player_slot_count,
2171            available_player_slots=fields.available_player_slots,
2172            available_alternate_slots=fields.available_alternate_slots,
2173            title=fields.title,
2174            date_created=fields.date_created,
2175            locale=fields.locale,
2176            last_modified=fields.last_modified,
2177            total_results=fields.total_results,
2178            scheduled_time=fields.scheduled_time,
2179            date_modified=fields.date_modified,
2180            members=self.deserialize_fireteam_members(payload),
2181            alternatives=self.deserialize_fireteam_members(payload, alternatives=True),
2182        )

Deserialize a JSON payload of a sequence of/fireteam information.

Parameters
Returns
  • An available fireteam object.
def deserialize_available_fireteams( self, data: collections.abc.Mapping[str, typing.Any]) -> collections.abc.Sequence[aiobungie.crates.fireteams.AvailableFireteam]:
2184    def deserialize_available_fireteams(
2185        self, data: typedefs.JSONObject
2186    ) -> collections.Sequence[fireteams.AvailableFireteam]:
2187        if raw_results := data.get("results"):
2188            fireteam_results = tuple(
2189                self.deserialize_available_fireteam(f) for f in raw_results
2190            )
2191        else:
2192            fireteam_results = ()
2193        return fireteam_results

Deserialize a JSON payload sequence of fireteam objects.

Parameters
Returns
  • collections.Sequence[aiobungie.crates.fireteams.AvailableFireteam]: A sequence of available fireteams.
def deserialize_fireteam_party( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.fireteams.FireteamParty:
2195    def deserialize_fireteam_party(
2196        self, payload: typedefs.JSONObject
2197    ) -> fireteams.FireteamParty:
2198        last_destination_hash: int | None = None
2199        if raw_dest_hash := payload.get("lastOrbitedDestinationHash"):
2200            last_destination_hash = int(raw_dest_hash)
2201
2202        return fireteams.FireteamParty(
2203            members=tuple(
2204                self._deserialize_fireteam_party_member(member)
2205                for member in payload["partyMembers"]
2206            ),
2207            activity=self._deserialize_fireteam_party_current_activity(
2208                payload["currentActivity"]
2209            ),
2210            settings=self._deserialize_fireteam_party_settings(payload["joinability"]),
2211            last_destination_hash=last_destination_hash,
2212            tracking=payload["tracking"],
2213        )

Deserialize a JSON payload of profileTransitory component response.

Parameters
Returns
def deserialize_seasonal_artifact( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.season.Artifact:
2256    def deserialize_seasonal_artifact(
2257        self, payload: typedefs.JSONObject
2258    ) -> season.Artifact:
2259        raw_artifact = payload["seasonalArtifact"]
2260
2261        points = raw_artifact["pointProgression"]
2262        points_prog = progressions.Progression(
2263            hash=points["progressionHash"],
2264            level=points["level"],
2265            cap=points["levelCap"],
2266            daily_limit=points["dailyLimit"],
2267            weekly_limit=points["weeklyLimit"],
2268            current_progress=points["currentProgress"],
2269            daily_progress=points["dailyProgress"],
2270            needed=points["progressToNextLevel"],
2271            next_level=points["nextLevelAt"],
2272        )
2273
2274        bonus = raw_artifact["powerBonusProgression"]
2275        power_bonus_prog = progressions.Progression(
2276            hash=bonus["progressionHash"],
2277            level=bonus["level"],
2278            cap=bonus["levelCap"],
2279            daily_limit=bonus["dailyLimit"],
2280            weekly_limit=bonus["weeklyLimit"],
2281            current_progress=bonus["currentProgress"],
2282            daily_progress=bonus["dailyProgress"],
2283            needed=bonus["progressToNextLevel"],
2284            next_level=bonus["nextLevelAt"],
2285        )
2286        return season.Artifact(
2287            hash=raw_artifact["artifactHash"],
2288            power_bonus=raw_artifact["powerBonus"],
2289            acquired_points=raw_artifact["pointsAcquired"],
2290            bonus=power_bonus_prog,
2291            points=points_prog,
2292        )

Deserialize a JSON payload of a Destiny 2 seasonal artifact information.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_profile_progression( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.profile.ProfileProgression:
2294    def deserialize_profile_progression(
2295        self, payload: typedefs.JSONObject
2296    ) -> profile.ProfileProgression:
2297        return profile.ProfileProgression(
2298            artifact=self.deserialize_seasonal_artifact(payload["data"]),
2299            checklist={
2300                int(check_id): checklists
2301                for check_id, checklists in payload["data"]["checklists"].items()
2302            },
2303        )

Deserialize a JSON payload of a profile progression component.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_instanced_item( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.items.ItemInstance:
2305    def deserialize_instanced_item(
2306        self, payload: typedefs.JSONObject
2307    ) -> items.ItemInstance:
2308        damage_type_hash: int | None = None
2309        if raw_damagetype_hash := payload.get("damageTypeHash"):
2310            damage_type_hash = int(raw_damagetype_hash)
2311
2312        required_hashes: typing.Optional[collections.Collection[int]] = None
2313        if raw_required_hashes := payload.get("unlockHashesRequiredToEquip"):
2314            required_hashes = tuple(int(raw_hash) for raw_hash in raw_required_hashes)
2315
2316        breaker_type: items.ItemBreakerType | None = None
2317        if raw_break_type := payload.get("breakerType"):
2318            breaker_type = items.ItemBreakerType(int(raw_break_type))
2319
2320        breaker_type_hash: int | None = None
2321        if raw_break_type_hash := payload.get("breakerTypeHash"):
2322            breaker_type_hash = int(raw_break_type_hash)
2323
2324        energy: items.ItemEnergy | None = None
2325        if raw_energy := payload.get("energy"):
2326            energy = self.deserialize_item_energy(raw_energy)
2327
2328        primary_stats = None
2329        if raw_primary_stats := payload.get("primaryStat"):
2330            primary_stats = self.deserialize_item_stats_view(raw_primary_stats)
2331
2332        return items.ItemInstance(
2333            damage_type=enums.DamageType(int(payload["damageType"])),
2334            damage_type_hash=damage_type_hash,
2335            primary_stat=primary_stats,
2336            item_level=int(payload["itemLevel"]),
2337            quality=int(payload["quality"]),
2338            is_equipped=payload["isEquipped"],
2339            can_equip=payload["canEquip"],
2340            equip_required_level=int(payload["equipRequiredLevel"]),
2341            required_equip_unlock_hashes=required_hashes,
2342            cant_equip_reason=int(payload["cannotEquipReason"]),
2343            breaker_type=breaker_type,
2344            breaker_type_hash=breaker_type_hash,
2345            energy=energy,
2346        )

Deserialize a JSON object into an instanced item.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_item_energy( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.items.ItemEnergy:
2348    def deserialize_item_energy(self, payload: typedefs.JSONObject) -> items.ItemEnergy:
2349        energy_hash: int | None = None
2350        if raw_energy_hash := payload.get("energyTypeHash"):
2351            energy_hash = int(raw_energy_hash)
2352
2353        return items.ItemEnergy(
2354            hash=energy_hash,
2355            type=items.ItemEnergyType(int(payload["energyType"])),
2356            capacity=int(payload["energyCapacity"]),
2357            used_energy=int(payload["energyUsed"]),
2358            unused_energy=int(payload["energyUnused"]),
2359        )
def deserialize_item_perk( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.items.ItemPerk:
2361    def deserialize_item_perk(self, payload: typedefs.JSONObject) -> items.ItemPerk:
2362        perk_hash: int | None = None
2363        if raw_perk_hash := payload.get("perkHash"):
2364            perk_hash = int(raw_perk_hash)
2365
2366        return items.ItemPerk(
2367            hash=perk_hash,
2368            icon=assets.Image(path=payload["iconPath"]),
2369            is_active=payload["isActive"],
2370            is_visible=payload["visible"],
2371        )
def deserialize_item_socket( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.items.ItemSocket:
2373    def deserialize_item_socket(self, payload: typedefs.JSONObject) -> items.ItemSocket:
2374        plug_hash: int | None = None
2375        if raw_plug_hash := payload.get("plugHash"):
2376            plug_hash = int(raw_plug_hash)
2377
2378        enable_fail_indexes: collections.Sequence[int] | None = None
2379        if raw_indexes := payload.get("enableFailIndexes"):
2380            enable_fail_indexes = tuple(int(index) for index in raw_indexes)
2381
2382        return items.ItemSocket(
2383            plug_hash=plug_hash,
2384            is_enabled=payload["isEnabled"],
2385            enable_fail_indexes=enable_fail_indexes,
2386            is_visible=payload.get("visible"),
2387        )
def deserialize_item_stats_view( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.items.ItemStatsView:
2389    def deserialize_item_stats_view(
2390        self, payload: typedefs.JSONObject
2391    ) -> items.ItemStatsView:
2392        return items.ItemStatsView(
2393            stat_hash=payload.get("statHash"), value=payload.get("value")
2394        )
def deserialize_plug_item_state( self, payload: collections.abc.Mapping[str, typing.Any]) -> aiobungie.crates.items.PlugItemState:
2396    def deserialize_plug_item_state(
2397        self, payload: typedefs.JSONObject
2398    ) -> items.PlugItemState:
2399        item_hash: int | None = None
2400        if raw_item_hash := payload.get("plugItemHash"):
2401            item_hash = int(raw_item_hash)
2402
2403        insert_fail_indexes: collections.Sequence[int] | None = None
2404        if raw_fail_indexes := payload.get("insertFailIndexes"):
2405            insert_fail_indexes = tuple(int(k) for k in raw_fail_indexes)
2406
2407        enable_fail_indexes: collections.Sequence[int] | None = None
2408        if raw_enabled_indexes := payload.get("enableFailIndexes"):
2409            enable_fail_indexes = tuple(int(k) for k in raw_enabled_indexes)
2410
2411        return items.PlugItemState(
2412            item_hash=item_hash,
2413            insert_fail_indexes=insert_fail_indexes,
2414            enable_fail_indexes=enable_fail_indexes,
2415            is_enabled=payload["enabled"],
2416            can_insert=payload["canInsert"],
2417        )
@typing.final
class FireteamActivity(builtins.int, aiobungie.Enum):
 65@typing.final
 66class FireteamActivity(int, enums.Enum):
 67    """An enum for the fireteam activities."""
 68
 69    ALL = 0
 70    CRUCIBLE = 2
 71    TRIALS_OF_OSIRIS = 3
 72    NIGHTFALL = 4
 73    ANY = 5
 74    GAMBIT = 6
 75    BLIND_WELL = 7
 76    NIGHTMARE_HUNTS = 12
 77    ALTARS_OF_SORROWS = 14
 78    DUNGEON = 15
 79    RAID_LW = 20
 80    RAID_GOS = 21
 81    RAID_DSC = 22
 82    EXO_CHALLENGE = 23
 83    S12_WRATHBORN = 24
 84    EMPIRE_HUNTS = 25
 85    S13_BATTLEGROUNDS = 26
 86    EXOTIC_QUEST = 27
 87    RAID_VOG = 28
 88    S14_EXPUNGE = 30
 89    S15_ASTRAL_ALIGNMENT = 31
 90    S15_SHATTERED_RELAM = 32
 91    SHATTERED_THRONE = 33
 92    PROPHECY = 34
 93    PIT_OF_HERESY = 35
 94    DOE = 36
 95    """Dares of Eternity."""
 96    DUNGEON_GOA = 37
 97    """Grasp of Avarice."""
 98    VOW_OF_THE_DISCPILE = 38
 99    CAMPAIGN = 39
100    WELLSPRING = 40
101    S16_BATTLEGROUNDS = 41
102    S17_NIGHTMARE_CONTAINMENT = 44
103    S17_SEVER = 45

An enum for the fireteam activities.

CRUCIBLE = <FireteamActivity.CRUCIBLE: 2>
TRIALS_OF_OSIRIS = <FireteamActivity.TRIALS_OF_OSIRIS: 3>
NIGHTFALL = <FireteamActivity.NIGHTFALL: 4>
GAMBIT = <FireteamActivity.GAMBIT: 6>
BLIND_WELL = <FireteamActivity.BLIND_WELL: 7>
NIGHTMARE_HUNTS = <FireteamActivity.NIGHTMARE_HUNTS: 12>
ALTARS_OF_SORROWS = <FireteamActivity.ALTARS_OF_SORROWS: 14>
DUNGEON = <FireteamActivity.DUNGEON: 15>
RAID_LW = <FireteamActivity.RAID_LW: 20>
RAID_GOS = <FireteamActivity.RAID_GOS: 21>
RAID_DSC = <FireteamActivity.RAID_DSC: 22>
EXO_CHALLENGE = <FireteamActivity.EXO_CHALLENGE: 23>
S12_WRATHBORN = <FireteamActivity.S12_WRATHBORN: 24>
EMPIRE_HUNTS = <FireteamActivity.EMPIRE_HUNTS: 25>
S13_BATTLEGROUNDS = <FireteamActivity.S13_BATTLEGROUNDS: 26>
EXOTIC_QUEST = <FireteamActivity.EXOTIC_QUEST: 27>
RAID_VOG = <FireteamActivity.RAID_VOG: 28>
S14_EXPUNGE = <FireteamActivity.S14_EXPUNGE: 30>
S15_ASTRAL_ALIGNMENT = <FireteamActivity.S15_ASTRAL_ALIGNMENT: 31>
S15_SHATTERED_RELAM = <FireteamActivity.S15_SHATTERED_RELAM: 32>
SHATTERED_THRONE = <FireteamActivity.SHATTERED_THRONE: 33>
PROPHECY = <FireteamActivity.PROPHECY: 34>
PIT_OF_HERESY = <FireteamActivity.PIT_OF_HERESY: 35>
DOE = <FireteamActivity.DOE: 36>

Dares of Eternity.

DUNGEON_GOA = <FireteamActivity.DUNGEON_GOA: 37>

Grasp of Avarice.

VOW_OF_THE_DISCPILE = <FireteamActivity.VOW_OF_THE_DISCPILE: 38>
CAMPAIGN = <FireteamActivity.CAMPAIGN: 39>
WELLSPRING = <FireteamActivity.WELLSPRING: 40>
S16_BATTLEGROUNDS = <FireteamActivity.S16_BATTLEGROUNDS: 41>
S17_NIGHTMARE_CONTAINMENT = <FireteamActivity.S17_NIGHTMARE_CONTAINMENT: 44>
S17_SEVER = <FireteamActivity.S17_SEVER: 45>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class FireteamDate(builtins.int, aiobungie.Enum):
129@typing.final
130class FireteamDate(int, enums.Enum):
131    """An enum for fireteam date ranges."""
132
133    ALL = 0
134    NOW = 1
135    TODAY = 2
136    TWO_DAYS = 3
137    THIS_WEEK = 4

An enum for fireteam date ranges.

ALL = <FireteamDate.ALL: 0>
NOW = <FireteamDate.NOW: 1>
TODAY = <FireteamDate.TODAY: 2>
TWO_DAYS = <FireteamDate.TWO_DAYS: 3>
THIS_WEEK = <FireteamDate.THIS_WEEK: 4>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class FireteamLanguage(builtins.str, aiobungie.Enum):
106@typing.final
107class FireteamLanguage(str, enums.Enum):
108    """An enum for fireteams languages filters."""
109
110    ALL = ""
111    ENGLISH = "en"
112    FRENCH = "fr"
113    ESPANOL = "es"
114    DEUTSCH = "de"
115    ITALIAN = "it"
116    JAPANESE = "ja"
117    PORTUGUESE = "pt-br"
118    RUSSIAN = "ru"
119    POLISH = "pl"
120    KOREAN = "ko"
121    # ? China
122    ZH_CHT = "zh-cht"
123    ZH_CHS = "zh-chs"
124
125    def __str__(self) -> str:
126        return str(self.value)

An enum for fireteams languages filters.

ENGLISH = <FireteamLanguage.ENGLISH: en>
FRENCH = <FireteamLanguage.FRENCH: fr>
ESPANOL = <FireteamLanguage.ESPANOL: es>
DEUTSCH = <FireteamLanguage.DEUTSCH: de>
ITALIAN = <FireteamLanguage.ITALIAN: it>
JAPANESE = <FireteamLanguage.JAPANESE: ja>
PORTUGUESE = <FireteamLanguage.PORTUGUESE: pt-br>
RUSSIAN = <FireteamLanguage.RUSSIAN: ru>
POLISH = <FireteamLanguage.POLISH: pl>
KOREAN = <FireteamLanguage.KOREAN: ko>
ZH_CHT = <FireteamLanguage.ZH_CHT: zh-cht>
ZH_CHS = <FireteamLanguage.ZH_CHS: zh-chs>
Inherited Members
Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
@typing.final
class FireteamPlatform(builtins.int, aiobungie.Enum):
52@typing.final
53class FireteamPlatform(int, enums.Enum):
54    """An enum for fireteam related to bungie fireteams.
55    This is different from the normal `aiobungie.MembershipType`.
56    """
57
58    ANY = 0
59    PSN_NETWORK = 1
60    XBOX_LIVE = 2
61    STEAM = 4
62    STADIA = 5

An enum for fireteam related to bungie fireteams. This is different from the normal aiobungie.MembershipType.

PSN_NETWORK = <FireteamPlatform.PSN_NETWORK: 1>
XBOX_LIVE = <FireteamPlatform.XBOX_LIVE: 2>
STEAM = <FireteamPlatform.STEAM: 4>
STADIA = <FireteamPlatform.STADIA: 5>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class Flag(enum.Flag):
 90class Flag(__enum.Flag):
 91    """Builtin Python enum flag with extra handlings."""
 92
 93    # Needs to type this here for mypy
 94    _value_: int
 95
 96    @property
 97    def name(self) -> str:
 98        if self._name_ is None:
 99            self._name_ = f"UNKNOWN {self._value_}"
100
101        return self._name_
102
103    @property
104    def value(self) -> int:
105        return self._value_
106
107    def __str__(self) -> str:
108        return self.name
109
110    def __repr__(self) -> str:
111        return f"<{type(self).__name__}.{self.name}: {self._value_!s}>"
112
113    def __int__(self) -> int:
114        return int(self.value)
115
116    def __or__(self, other: Flag | int) -> Flag:
117        return self.__class__(self._value_ | int(other))
118
119    def __xor__(self, other: Flag | int) -> Flag:
120        return self.__class__(self._value_ ^ int(other))
121
122    def __and__(self, other: Flag | int) -> Flag:
123        return self.__class__(other & int(other))
124
125    def __invert__(self) -> Flag:
126        return self.__class__(~self._value_)
127
128    def __contains__(self, other: Flag | int) -> bool:
129        return self.value & int(other) == int(other)

Builtin Python enum flag with extra handlings.

name: str
 96    @property
 97    def name(self) -> str:
 98        if self._name_ is None:
 99            self._name_ = f"UNKNOWN {self._value_}"
100
101        return self._name_

The name of the Enum member.

value: int
103    @property
104    def value(self) -> int:
105        return self._value_

The value of the Enum member.

@attrs.define(auto_exc=True)
class Forbidden(aiobungie.HTTPException):
136@attrs.define(auto_exc=True)
137class Forbidden(HTTPException):
138    """Exception that's raised for when status code 403 occurs."""
139
140    http_status: http.HTTPStatus = attrs.field(
141        default=http.HTTPStatus.FORBIDDEN, init=False
142    )

Exception that's raised for when status code 403 occurs.

Forbidden( *, error_code: int, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.throttle_seconds = throttle_seconds
 5    self.url = url
 6    self.body = body
 7    self.headers = headers
 8    self.message = message
 9    self.error_status = error_status
10    self.message_data = message_data
11    self.http_status = attr_dict['http_status'].default
12    BaseException.__init__(self, self.error_code,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class Forbidden.

http_status: http.HTTPStatus

The request response http status.

Inherited Members
HTTPException
error_code
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
args
@typing.final
class GameMode(builtins.int, aiobungie.Enum):
258@typing.final
259class GameMode(int, Enum):
260    """An Enum for all available gamemodes in Destiny 2."""
261
262    NONE = 0
263    STORY = 2
264    STRIKE = 3
265    RAID = 4
266    ALLPVP = 5
267    PATROL = 6
268    ALLPVE = 7
269    RESERVED9 = 9
270    CONTROL = 10
271    RESERVED11 = 11
272    CLASH = 12
273    RESERVED13 = 13
274    CRIMSONDOUBLES = 15
275    NIGHTFALL = 16
276    HEROICNIGHTFALL = 17
277    ALLSTRIKES = 18
278    IRONBANNER = 19
279    RESERVED20 = 20
280    RESERVED21 = 21
281    RESERVED22 = 22
282    RESERVED24 = 24
283    ALLMAYHEM = 25
284    RESERVED26 = 26
285    RESERVED27 = 27
286    RESERVED28 = 28
287    RESERVED29 = 29
288    RESERVED30 = 30
289    SUPREMACY = 31
290    PRIVATEMATCHESALL = 32
291    SURVIVAL = 37
292    COUNTDOWN = 38
293    TRIALSOFTHENINE = 39
294    SOCIAL = 40
295    TRIALSCOUNTDOWN = 41
296    TRIALSSURVIVAL = 42
297    IRONBANNERCONTROL = 43
298    IRONBANNERCLASH = 44
299    IRONBANNERSUPREMACY = 45
300    SCOREDNIGHTFALL = 46
301    SCOREDHEROICNIGHTFALL = 47
302    RUMBLE = 48
303    ALLDOUBLES = 49
304    DOUBLES = 50
305    PRIVATEMATCHESCLASH = 51
306    PRIVATEMATCHESCONTROL = 52
307    PRIVATEMATCHESSUPREMACY = 53
308    PRIVATEMATCHESCOUNTDOWN = 54
309    PRIVATEMATCHESSURVIVAL = 55
310    PRIVATEMATCHESMAYHEM = 56
311    PRIVATEMATCHESRUMBLE = 57
312    HEROICADVENTURE = 58
313    SHOWDOWN = 59
314    LOCKDOWN = 60
315    SCORCHED = 61
316    SCORCHEDTEAM = 62
317    GAMBIT = 63
318    ALLPVECOMPETITIVE = 64
319    BREAKTHROUGH = 65
320    BLACKARMORYRUN = 66
321    SALVAGE = 67
322    IRONBANNERSALVAGE = 68
323    PVPCOMPETITIVE = 69
324    PVPQUICKPLAY = 70
325    CLASHQUICKPLAY = 71
326    CLASHCOMPETITIVE = 72
327    CONTROLQUICKPLAY = 73
328    CONTROLCOMPETITIVE = 74
329    GAMBITPRIME = 75
330    RECKONING = 76
331    MENAGERIE = 77
332    VEXOFFENSIVE = 78
333    NIGHTMAREHUNT = 79
334    ELIMINATION = 80
335    MOMENTUM = 81
336    DUNGEON = 82
337    SUNDIAL = 83
338    TRIALS_OF_OSIRIS = 84
339    DARES = 85
340    OFFENSIVE = 86
341    LOSTSECTOR = 87
342    RIFT = 88
343    ZONECONTROL = 89
344    IRONBANNERRIFT = 90

An Enum for all available gamemodes in Destiny 2.

NONE = <GameMode.NONE: 0>
STORY = <GameMode.STORY: 2>
STRIKE = <GameMode.STRIKE: 3>
RAID = <GameMode.RAID: 4>
ALLPVP = <GameMode.ALLPVP: 5>
PATROL = <GameMode.PATROL: 6>
ALLPVE = <GameMode.ALLPVE: 7>
RESERVED9 = <GameMode.RESERVED9: 9>
CONTROL = <GameMode.CONTROL: 10>
RESERVED11 = <GameMode.RESERVED11: 11>
CLASH = <GameMode.CLASH: 12>
RESERVED13 = <GameMode.RESERVED13: 13>
CRIMSONDOUBLES = <GameMode.CRIMSONDOUBLES: 15>
NIGHTFALL = <GameMode.NIGHTFALL: 16>
HEROICNIGHTFALL = <GameMode.HEROICNIGHTFALL: 17>
ALLSTRIKES = <GameMode.ALLSTRIKES: 18>
IRONBANNER = <GameMode.IRONBANNER: 19>
RESERVED20 = <GameMode.RESERVED20: 20>
RESERVED21 = <GameMode.RESERVED21: 21>
RESERVED22 = <GameMode.RESERVED22: 22>
RESERVED24 = <GameMode.RESERVED24: 24>
ALLMAYHEM = <GameMode.ALLMAYHEM: 25>
RESERVED26 = <GameMode.RESERVED26: 26>
RESERVED27 = <GameMode.RESERVED27: 27>
RESERVED28 = <GameMode.RESERVED28: 28>
RESERVED29 = <GameMode.RESERVED29: 29>
RESERVED30 = <GameMode.RESERVED30: 30>
SUPREMACY = <GameMode.SUPREMACY: 31>
PRIVATEMATCHESALL = <GameMode.PRIVATEMATCHESALL: 32>
SURVIVAL = <GameMode.SURVIVAL: 37>
COUNTDOWN = <GameMode.COUNTDOWN: 38>
TRIALSOFTHENINE = <GameMode.TRIALSOFTHENINE: 39>
SOCIAL = <GameMode.SOCIAL: 40>
TRIALSCOUNTDOWN = <GameMode.TRIALSCOUNTDOWN: 41>
TRIALSSURVIVAL = <GameMode.TRIALSSURVIVAL: 42>
IRONBANNERCONTROL = <GameMode.IRONBANNERCONTROL: 43>
IRONBANNERCLASH = <GameMode.IRONBANNERCLASH: 44>
IRONBANNERSUPREMACY = <GameMode.IRONBANNERSUPREMACY: 45>
SCOREDNIGHTFALL = <GameMode.SCOREDNIGHTFALL: 46>
SCOREDHEROICNIGHTFALL = <GameMode.SCOREDHEROICNIGHTFALL: 47>
RUMBLE = <GameMode.RUMBLE: 48>
ALLDOUBLES = <GameMode.ALLDOUBLES: 49>
DOUBLES = <GameMode.DOUBLES: 50>
PRIVATEMATCHESCLASH = <GameMode.PRIVATEMATCHESCLASH: 51>
PRIVATEMATCHESCONTROL = <GameMode.PRIVATEMATCHESCONTROL: 52>
PRIVATEMATCHESSUPREMACY = <GameMode.PRIVATEMATCHESSUPREMACY: 53>
PRIVATEMATCHESCOUNTDOWN = <GameMode.PRIVATEMATCHESCOUNTDOWN: 54>
PRIVATEMATCHESSURVIVAL = <GameMode.PRIVATEMATCHESSURVIVAL: 55>
PRIVATEMATCHESMAYHEM = <GameMode.PRIVATEMATCHESMAYHEM: 56>
PRIVATEMATCHESRUMBLE = <GameMode.PRIVATEMATCHESRUMBLE: 57>
HEROICADVENTURE = <GameMode.HEROICADVENTURE: 58>
SHOWDOWN = <GameMode.SHOWDOWN: 59>
LOCKDOWN = <GameMode.LOCKDOWN: 60>
SCORCHED = <GameMode.SCORCHED: 61>
SCORCHEDTEAM = <GameMode.SCORCHEDTEAM: 62>
GAMBIT = <GameMode.GAMBIT: 63>
ALLPVECOMPETITIVE = <GameMode.ALLPVECOMPETITIVE: 64>
BREAKTHROUGH = <GameMode.BREAKTHROUGH: 65>
BLACKARMORYRUN = <GameMode.BLACKARMORYRUN: 66>
SALVAGE = <GameMode.SALVAGE: 67>
IRONBANNERSALVAGE = <GameMode.IRONBANNERSALVAGE: 68>
PVPCOMPETITIVE = <GameMode.PVPCOMPETITIVE: 69>
PVPQUICKPLAY = <GameMode.PVPQUICKPLAY: 70>
CLASHQUICKPLAY = <GameMode.CLASHQUICKPLAY: 71>
CLASHCOMPETITIVE = <GameMode.CLASHCOMPETITIVE: 72>
CONTROLQUICKPLAY = <GameMode.CONTROLQUICKPLAY: 73>
CONTROLCOMPETITIVE = <GameMode.CONTROLCOMPETITIVE: 74>
GAMBITPRIME = <GameMode.GAMBITPRIME: 75>
RECKONING = <GameMode.RECKONING: 76>
MENAGERIE = <GameMode.MENAGERIE: 77>
VEXOFFENSIVE = <GameMode.VEXOFFENSIVE: 78>
NIGHTMAREHUNT = <GameMode.NIGHTMAREHUNT: 79>
ELIMINATION = <GameMode.ELIMINATION: 80>
MOMENTUM = <GameMode.MOMENTUM: 81>
DUNGEON = <GameMode.DUNGEON: 82>
SUNDIAL = <GameMode.SUNDIAL: 83>
TRIALS_OF_OSIRIS = <GameMode.TRIALS_OF_OSIRIS: 84>
DARES = <GameMode.DARES: 85>
OFFENSIVE = <GameMode.OFFENSIVE: 86>
LOSTSECTOR = <GameMode.LOSTSECTOR: 87>
RIFT = <GameMode.RIFT: 88>
ZONECONTROL = <GameMode.ZONECONTROL: 89>
IRONBANNERRIFT = <GameMode.IRONBANNERRIFT: 90>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class GatingScope(builtins.int, aiobungie.Enum):
57@typing.final
58class GatingScope(int, enums.Enum):
59    """An enum represents restrictive type of gating that is being performed by an entity.
60
61    This is useful as a shortcut to avoid a lot of lookups when determining whether the gating on an Entity
62    applies to everyone equally, or to their specific Profile or Character states.
63    """
64
65    NONE = 0
66    GLOBAL = 1
67    CLAN = 2
68    PROFILE = 3
69    CHARACTER = 4
70    ITEM = 5
71    ASSUMED_WORST_CASE = 6

An enum represents restrictive type of gating that is being performed by an entity.

This is useful as a shortcut to avoid a lot of lookups when determining whether the gating on an Entity applies to everyone equally, or to their specific Profile or Character states.

NONE = <GatingScope.NONE: 0>
GLOBAL = <GatingScope.GLOBAL: 1>
CLAN = <GatingScope.CLAN: 2>
PROFILE = <GatingScope.PROFILE: 3>
CHARACTER = <GatingScope.CHARACTER: 4>
ITEM = <GatingScope.ITEM: 5>
ASSUMED_WORST_CASE = <GatingScope.ASSUMED_WORST_CASE: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Gender(builtins.int, aiobungie.Enum):
476@typing.final
477class Gender(int, Enum):
478    """An Enum for Destiny Genders."""
479
480    MALE = 0
481    FEMALE = 1
482    UNKNOWN = 2

An Enum for Destiny Genders.

MALE = <Gender.MALE: 0>
FEMALE = <Gender.FEMALE: 1>
UNKNOWN = <Gender.UNKNOWN: 2>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class GroupType(builtins.int, aiobungie.Enum):
645@typing.final
646class GroupType(int, Enum):
647    """An enums for the known bungie group types."""
648
649    GENERAL = 0
650    CLAN = 1

An enums for the known bungie group types.

GENERAL = <GroupType.GENERAL: 0>
CLAN = <GroupType.CLAN: 1>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class HTTPError(aiobungie.AiobungieError):
75@attrs.define(auto_exc=True)
76class HTTPError(AiobungieError):
77    """Base HTTP request errors exception."""
78
79    message: str
80    """The error message."""
81
82    http_status: http.HTTPStatus
83    """The response status."""

Base HTTP request errors exception.

HTTPError(message: str, http_status: http.HTTPStatus)
2def __init__(self, message, http_status):
3    self.message = message
4    self.http_status = http_status
5    BaseException.__init__(self, self.message,self.http_status)

Method generated by attrs for class HTTPError.

message: str

The error message.

http_status: http.HTTPStatus

The response status.

Inherited Members
builtins.BaseException
with_traceback
args
@attrs.define(auto_exc=True, kw_only=True)
class HTTPException(aiobungie.HTTPError):
 86@attrs.define(auto_exc=True, kw_only=True)
 87class HTTPException(HTTPError):
 88    """An in-depth HTTP exception that's raised with more information."""
 89
 90    error_code: int
 91    """The returned Bungie error status code."""
 92
 93    http_status: http.HTTPStatus
 94    """The request response http status."""
 95
 96    throttle_seconds: int
 97    """The Bungie response throttle seconds."""
 98
 99    url: typedefs.StrOrURL | None
100    """The URL/endpoint caused this error."""
101
102    body: typing.Any
103    """The response body."""
104
105    headers: multidict.CIMultiDictProxy[str]
106    """The response headers."""
107
108    message: str
109    """A Bungie human readable message describes the cause of the error."""
110
111    error_status: str
112    """A Bungie short error status describes the cause of the error."""
113
114    message_data: dict[str, str]
115    """A dict of string key, value that includes each cause of the error
116    to a message describes information about that error.
117    """
118
119    def __str__(self) -> str:
120        status_name, status_value = (
121            self.http_status.name.replace("_", "").title(),
122            self.http_status.value,
123        )
124        return (
125            f"{status_name}: " + "("
126            f"""
127            http_status: {status_value},
128            message: {self.message if self.message else 'UNDEFINED'},
129            error_status: {self.error_status if self.error_status else 'UNDEFINED'},
130            url: {self.url if self.url else 'UNDEFINED'},
131            message_data: {self.message_data}
132        """ + ")"
133        )

An in-depth HTTP exception that's raised with more information.

HTTPException( *, error_code: int, http_status: http.HTTPStatus, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, http_status, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.http_status = http_status
 5    self.throttle_seconds = throttle_seconds
 6    self.url = url
 7    self.body = body
 8    self.headers = headers
 9    self.message = message
10    self.error_status = error_status
11    self.message_data = message_data
12    BaseException.__init__(self, self.error_code,self.http_status,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class HTTPException.

error_code: int

The returned Bungie error status code.

http_status: http.HTTPStatus

The request response http status.

throttle_seconds: int

The Bungie response throttle seconds.

url: Union[str, yarl.URL, NoneType]

The URL/endpoint caused this error.

body: Any

The response body.

headers: multidict._multidict.CIMultiDictProxy[str]

The response headers.

message: str

A Bungie human readable message describes the cause of the error.

error_status: str

A Bungie short error status describes the cause of the error.

message_data: dict[str, str]

A dict of string key, value that includes each cause of the error to a message describes information about that error.

Inherited Members
builtins.BaseException
with_traceback
args
@attrs.frozen(kw_only=True, weakref_slot=False)
class Image:
 76@attrs.frozen(kw_only=True, weakref_slot=False)
 77class Image:
 78    """Representation of an image/avatar/picture at Bungie.
 79
 80    Example
 81    -------
 82    ```py
 83    from aiobungie import Image
 84    img = Image("img/destiny_content/pgcr/raid_eclipse.jpg")
 85    print(img)
 86    # https://www.bungie.net/img/destiny_content/pgcr/raid_eclipse.jpg
 87
 88    # Stream the image.
 89    async for chunk in img:
 90        # Byte chunks of the image.
 91        print(chunk)
 92
 93    # Save the image to a file.
 94    await img.save("file_name", "/my/path/to/save/to", "jpeg")
 95    ```
 96
 97    Parameters
 98    ----------
 99    path : `str | None`
100        The path to the image..
101    """
102
103    path: str = attrs.field()
104
105    @property
106    def is_missing(self) -> bool:
107        return not self.path
108
109    @property
110    def url(self) -> str:
111        """The URL to the image."""
112        return self.create_url()
113
114    @staticmethod
115    def default() -> str:
116        """Returns the path to the missing Bungie image.
117
118        Note
119        ----
120        This returns the path only, If you want an actual image object use `Image.default_or_else()`
121        """
122        return "/img/misc/missing_icon_d2.png"
123
124    @classmethod
125    def default_or_else(cls, path: str | None = None) -> Self:
126        """Return the default image if `path` was `None` otherwise an `Image` object.
127
128        Example
129        -------
130        ```py
131        img = Image.default_or_else(None)
132        print(img.url()) # https://www.bungie.net/img/misc/missing_icon_d2.png
133
134        img = Image.default_or_else("/some_path/image.png")
135        ```
136        """
137        return cls(path=path or Image.default())
138
139    def create_url(self) -> str:
140        """Creates a full URL to the image path.
141
142        Returns
143        -------
144        str
145            The URL to the image.
146        """
147        return f"{url.BASE}/{self.path if self.path else self.default()}"
148
149    async def save(
150        self,
151        file_name: str,
152        path: pathlib.Path | str,
153        /,
154        mime_type: MimeType | str = MimeType.JPEG,
155    ) -> None:
156        """Saves the image to a file.
157
158        Parameters
159        ----------
160        file_name : `str`
161            A name for the file to save the image to.
162        path : `pathlib.Path | str`
163            A path tp save the image to.
164
165        Other Parameters
166        ----------------
167        mime_type : `MimeType | str`
168            MIME type of the image. Defaults to JPEG.
169
170        Raises
171        ------
172        `FileNotFoundError`
173            If the path provided does not exist.
174        `RuntimeError`
175            If the image could not be saved.
176        `PermissionError`
177            If the path provided is not writable or does not have write permissions.
178        """
179        if isinstance(path, pathlib.Path) and not path.exists():
180            raise FileNotFoundError(f"File does not exist: {path!r}")
181
182        if self.is_missing:
183            return
184
185        path = pathlib.Path(path)
186
187        loop = helpers.get_or_make_loop()
188        pool = concurrent.futures.ThreadPoolExecutor()
189
190        try:
191            with pool:
192                await loop.run_in_executor(
193                    pool, _write, path, file_name, mime_type, await self.read()
194                )
195                _LOGGER.info("Saved image to %s", file_name)
196
197        except asyncio.CancelledError:
198            pass
199
200        except Exception as err:
201            raise RuntimeError("Encountered an error while saving image.") from err
202
203    async def read(self) -> bytes:
204        """Read this image bytes.
205
206        Returns
207        -------
208        `bytes`
209            The bytes of this image.
210        """
211        client_session = aiohttp.ClientSession()
212
213        byte = b""
214        try:
215            await client_session.__aenter__()
216            response = await client_session.get(self.create_url())
217
218            if 300 >= response.status >= 200:
219                byte = await response.read()
220
221        except Exception as exc:
222            raise RuntimeError(f"Failed to read image: {exc}") from None
223        finally:
224            await client_session.__aexit__(None, None, None)
225        return byte
226
227    async def iter(self) -> collections.AsyncGenerator[bytes, None]:
228        """Iterates over the image bytes lazily.
229
230        Example
231        -------
232        import aiobungie
233
234        resource = aiobungie.Image("img/misc/missing_icon_d2.png")
235        async for chunk in resource.iter():
236            print(chunk)
237
238        Returns
239        -------
240        `collections.AsyncGenerator[bytes, None]`
241            An async generator of the image bytes.
242        """
243
244        async for chunk in self:
245            yield chunk
246
247    def __repr__(self) -> str:
248        return f"Image(url={self.create_url()})"
249
250    def __str__(self) -> str:
251        return self.create_url()
252
253    def __aiter__(self) -> Image:
254        return self
255
256    async def __anext__(self) -> bytes:
257        return await self.read()
258
259    def __await__(self) -> collections.Generator[None, None, bytes]:
260        return self.__anext__().__await__()

Representation of an image/avatar/picture at Bungie.

Example
from aiobungie import Image
img = Image("img/destiny_content/pgcr/raid_eclipse.jpg")
print(img)
# https://www.bungie.net/img/destiny_content/pgcr/raid_eclipse.jpg

# Stream the image.
async for chunk in img:
    # Byte chunks of the image.
    print(chunk)

# Save the image to a file.
await img.save("file_name", "/my/path/to/save/to", "jpeg")
Parameters
  • path (str | None): The path to the image..
Image(*, path: str)
2def __init__(self, *, path):
3    _setattr = _cached_setattr_get(self)
4    _setattr('path', path)

Method generated by attrs for class Image.

path: str
is_missing: bool
105    @property
106    def is_missing(self) -> bool:
107        return not self.path
url: str
109    @property
110    def url(self) -> str:
111        """The URL to the image."""
112        return self.create_url()

The URL to the image.

@staticmethod
def default() -> str:
114    @staticmethod
115    def default() -> str:
116        """Returns the path to the missing Bungie image.
117
118        Note
119        ----
120        This returns the path only, If you want an actual image object use `Image.default_or_else()`
121        """
122        return "/img/misc/missing_icon_d2.png"

Returns the path to the missing Bungie image.

Note

This returns the path only, If you want an actual image object use Image.default_or_else()

@classmethod
def default_or_else(cls, path: str | None = None) -> typing_extensions.Self:
124    @classmethod
125    def default_or_else(cls, path: str | None = None) -> Self:
126        """Return the default image if `path` was `None` otherwise an `Image` object.
127
128        Example
129        -------
130        ```py
131        img = Image.default_or_else(None)
132        print(img.url()) # https://www.bungie.net/img/misc/missing_icon_d2.png
133
134        img = Image.default_or_else("/some_path/image.png")
135        ```
136        """
137        return cls(path=path or Image.default())

Return the default image if path was None otherwise an Image object.

Example
img = Image.default_or_else(None)
print(img.url()) # https://www.bungie.net/img/misc/missing_icon_d2.png

img = Image.default_or_else("/some_path/image.png")
def create_url(self) -> str:
139    def create_url(self) -> str:
140        """Creates a full URL to the image path.
141
142        Returns
143        -------
144        str
145            The URL to the image.
146        """
147        return f"{url.BASE}/{self.path if self.path else self.default()}"

Creates a full URL to the image path.

Returns
  • str: The URL to the image.
async def save( self, file_name: str, path: pathlib.Path | str, /, mime_type: aiobungie.internal.assets.MimeType | str = <MimeType.JPEG: jpeg>) -> None:
149    async def save(
150        self,
151        file_name: str,
152        path: pathlib.Path | str,
153        /,
154        mime_type: MimeType | str = MimeType.JPEG,
155    ) -> None:
156        """Saves the image to a file.
157
158        Parameters
159        ----------
160        file_name : `str`
161            A name for the file to save the image to.
162        path : `pathlib.Path | str`
163            A path tp save the image to.
164
165        Other Parameters
166        ----------------
167        mime_type : `MimeType | str`
168            MIME type of the image. Defaults to JPEG.
169
170        Raises
171        ------
172        `FileNotFoundError`
173            If the path provided does not exist.
174        `RuntimeError`
175            If the image could not be saved.
176        `PermissionError`
177            If the path provided is not writable or does not have write permissions.
178        """
179        if isinstance(path, pathlib.Path) and not path.exists():
180            raise FileNotFoundError(f"File does not exist: {path!r}")
181
182        if self.is_missing:
183            return
184
185        path = pathlib.Path(path)
186
187        loop = helpers.get_or_make_loop()
188        pool = concurrent.futures.ThreadPoolExecutor()
189
190        try:
191            with pool:
192                await loop.run_in_executor(
193                    pool, _write, path, file_name, mime_type, await self.read()
194                )
195                _LOGGER.info("Saved image to %s", file_name)
196
197        except asyncio.CancelledError:
198            pass
199
200        except Exception as err:
201            raise RuntimeError("Encountered an error while saving image.") from err

Saves the image to a file.

Parameters
  • file_name (str): A name for the file to save the image to.
  • path (pathlib.Path | str): A path tp save the image to.
Other Parameters
  • mime_type (MimeType | str): MIME type of the image. Defaults to JPEG.
Raises
  • FileNotFoundError: If the path provided does not exist.
  • RuntimeError: If the image could not be saved.
  • PermissionError: If the path provided is not writable or does not have write permissions.
async def read(self) -> bytes:
203    async def read(self) -> bytes:
204        """Read this image bytes.
205
206        Returns
207        -------
208        `bytes`
209            The bytes of this image.
210        """
211        client_session = aiohttp.ClientSession()
212
213        byte = b""
214        try:
215            await client_session.__aenter__()
216            response = await client_session.get(self.create_url())
217
218            if 300 >= response.status >= 200:
219                byte = await response.read()
220
221        except Exception as exc:
222            raise RuntimeError(f"Failed to read image: {exc}") from None
223        finally:
224            await client_session.__aexit__(None, None, None)
225        return byte

Read this image bytes.

Returns
  • bytes: The bytes of this image.
async def iter(self) -> collections.abc.AsyncGenerator[bytes, None]:
227    async def iter(self) -> collections.AsyncGenerator[bytes, None]:
228        """Iterates over the image bytes lazily.
229
230        Example
231        -------
232        import aiobungie
233
234        resource = aiobungie.Image("img/misc/missing_icon_d2.png")
235        async for chunk in resource.iter():
236            print(chunk)
237
238        Returns
239        -------
240        `collections.AsyncGenerator[bytes, None]`
241            An async generator of the image bytes.
242        """
243
244        async for chunk in self:
245            yield chunk

Iterates over the image bytes lazily.

Example

import aiobungie

resource = aiobungie.Image("img/misc/missing_icon_d2.png") async for chunk in resource.iter(): print(chunk)

Returns
  • collections.AsyncGenerator[bytes, None]: An async generator of the image bytes.
@attrs.define(auto_exc=True)
class InternalServerError(aiobungie.HTTPException):
240@attrs.define(auto_exc=True)
241class InternalServerError(HTTPException):
242    """Raised for 5xx internal server errors."""

Raised for 5xx internal server errors.

InternalServerError( *, error_code: int, http_status: http.HTTPStatus, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, http_status, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.http_status = http_status
 5    self.throttle_seconds = throttle_seconds
 6    self.url = url
 7    self.body = body
 8    self.headers = headers
 9    self.message = message
10    self.error_status = error_status
11    self.message_data = message_data
12    BaseException.__init__(self, self.error_code,self.http_status,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class InternalServerError.

Inherited Members
HTTPException
error_code
http_status
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
args
@typing.final
class ItemBindStatus(builtins.int, aiobungie.Enum):
711@typing.final
712class ItemBindStatus(int, Enum):
713    """An enum for Destiny 2 items bind status."""
714
715    NOT_BOUND = 0
716    BOUND_TO_CHARACTER = 1
717    BOUND_TO_ACCOUNT = 2
718    BOUNT_TO_GUILD = 3

An enum for Destiny 2 items bind status.

NOT_BOUND = <ItemBindStatus.NOT_BOUND: 0>
BOUND_TO_CHARACTER = <ItemBindStatus.BOUND_TO_CHARACTER: 1>
BOUND_TO_ACCOUNT = <ItemBindStatus.BOUND_TO_ACCOUNT: 2>
BOUNT_TO_GUILD = <ItemBindStatus.BOUNT_TO_GUILD: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemLocation(builtins.int, aiobungie.Enum):
721@typing.final
722class ItemLocation(int, Enum):
723    """An enum for Destiny 2 items location."""
724
725    UNKNOWN = 0
726    INVENTORY = 1
727    VAULT = 2
728    VENDOR = 3
729    POSTMASTER = 4

An enum for Destiny 2 items location.

UNKNOWN = <ItemLocation.UNKNOWN: 0>
INVENTORY = <ItemLocation.INVENTORY: 1>
VAULT = <ItemLocation.VAULT: 2>
VENDOR = <ItemLocation.VENDOR: 3>
POSTMASTER = <ItemLocation.POSTMASTER: 4>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemState(aiobungie.Flag):
746@typing.final
747class ItemState(Flag):
748    """An enum for Destiny 2 item states."""
749
750    NONE = 0
751    LOCKED = 1 << 0
752    TRACKED = 1 << 1
753    MASTERWORKED = 1 << 2
754    CRAFTED = 1 << 3
755    """If this bit is set, the item has been 'crafted' by the player."""
756    HIGHLITED_OBJECTIVE = 1 << 4
757    """If this bit is set, the item is a 'highlighted' objective."""

An enum for Destiny 2 item states.

NONE = <ItemState.NONE: 0>
LOCKED = <ItemState.LOCKED: 1>
TRACKED = <ItemState.TRACKED: 2>
MASTERWORKED = <ItemState.MASTERWORKED: 4>
CRAFTED = <ItemState.CRAFTED: 8>

If this bit is set, the item has been 'crafted' by the player.

HIGHLITED_OBJECTIVE = <ItemState.HIGHLITED_OBJECTIVE: 16>

If this bit is set, the item is a 'highlighted' objective.

Inherited Members
Flag
name
value
@typing.final
class ItemSubType(builtins.int, aiobungie.Enum):
578@typing.final
579class ItemSubType(int, Enum):
580    """An enum for Destiny 2 inventory items subtype."""
581
582    NONE = 0
583    AUTORIFLE = 6
584    SHOTGUN = 7
585    MACHINEGUN = 8
586    HANDCANNON = 9
587    ROCKETLAUNCHER = 10
588    FUSIONRIFLE = 11
589    SNIPERRIFLE = 12
590    PULSERIFLE = 13
591    SCOUTRIFLE = 14
592    SIDEARM = 17
593    SWORD = 18
594    MASK = 19
595    SHADER = 20
596    ORNAMENT = 21
597    FUSIONRIFLELINE = 22
598    GRENADELAUNCHER = 23
599    SUBMACHINEGUN = 24
600    TRACERIFLE = 25
601    HELMETARMOR = 26
602    GAUNTLETSARMOR = 27
603    CHESTARMOR = 28
604    LEGARMOR = 29
605    CLASSARMOR = 30
606    BOW = 31
607    DUMMYREPEATABLEBOUNTY = 32

An enum for Destiny 2 inventory items subtype.

NONE = <ItemSubType.NONE: 0>
AUTORIFLE = <ItemSubType.AUTORIFLE: 6>
SHOTGUN = <ItemSubType.SHOTGUN: 7>
MACHINEGUN = <ItemSubType.MACHINEGUN: 8>
HANDCANNON = <ItemSubType.HANDCANNON: 9>
ROCKETLAUNCHER = <ItemSubType.ROCKETLAUNCHER: 10>
FUSIONRIFLE = <ItemSubType.FUSIONRIFLE: 11>
SNIPERRIFLE = <ItemSubType.SNIPERRIFLE: 12>
PULSERIFLE = <ItemSubType.PULSERIFLE: 13>
SCOUTRIFLE = <ItemSubType.SCOUTRIFLE: 14>
SIDEARM = <ItemSubType.SIDEARM: 17>
SWORD = <ItemSubType.SWORD: 18>
MASK = <ItemSubType.MASK: 19>
SHADER = <ItemSubType.SHADER: 20>
ORNAMENT = <ItemSubType.ORNAMENT: 21>
FUSIONRIFLELINE = <ItemSubType.FUSIONRIFLELINE: 22>
GRENADELAUNCHER = <ItemSubType.GRENADELAUNCHER: 23>
SUBMACHINEGUN = <ItemSubType.SUBMACHINEGUN: 24>
TRACERIFLE = <ItemSubType.TRACERIFLE: 25>
HELMETARMOR = <ItemSubType.HELMETARMOR: 26>
GAUNTLETSARMOR = <ItemSubType.GAUNTLETSARMOR: 27>
CHESTARMOR = <ItemSubType.CHESTARMOR: 28>
LEGARMOR = <ItemSubType.LEGARMOR: 29>
CLASSARMOR = <ItemSubType.CLASSARMOR: 30>
BOW = <ItemSubType.BOW: 31>
DUMMYREPEATABLEBOUNTY = <ItemSubType.DUMMYREPEATABLEBOUNTY: 32>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemTier(builtins.int, aiobungie.Enum):
610@typing.final
611class ItemTier(int, Enum):
612    """An enum for a Destiny 2 item tier."""
613
614    NONE = 0
615    BASIC = 3340296461
616    COMMON = 2395677314
617    RARE = 2127292149
618    LEGENDERY = 4008398120
619    EXOTIC = 2759499571

An enum for a Destiny 2 item tier.

NONE = <ItemTier.NONE: 0>
BASIC = <ItemTier.BASIC: 3340296461>
COMMON = <ItemTier.COMMON: 2395677314>
RARE = <ItemTier.RARE: 2127292149>
LEGENDERY = <ItemTier.LEGENDERY: 4008398120>
EXOTIC = <ItemTier.EXOTIC: 2759499571>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemType(builtins.int, aiobungie.Enum):
545@typing.final
546class ItemType(int, Enum):
547    """Enums for Destiny2's item types."""
548
549    NONE = 0
550    CURRENCY = 1
551    ARMOR = 2
552    WEAPON = 3
553    MESSAGE = 7
554    ENGRAM = 8
555    CONSUMABLE = 9
556    EXCHANGEMATERIAL = 10
557    MISSIONREWARD = 11
558    QUESTSTEP = 12
559    QUESTSTEPCOMPLETE = 13
560    EMBLEM = 14
561    QUEST = 15
562    SUBCLASS = 16
563    CLANBANNER = 17
564    AURA = 18
565    MOD = 19
566    DUMMY = 20
567    SHIP = 21
568    VEHICLE = 22
569    EMOTE = 23
570    GHOST = 24
571    PACKAGE = 25
572    BOUNTY = 26
573    WRAPPER = 27
574    SEASONALARTIFACT = 28
575    FINISHER = 29

Enums for Destiny2's item types.

NONE = <ItemType.NONE: 0>
CURRENCY = <ItemType.CURRENCY: 1>
ARMOR = <ItemType.ARMOR: 2>
WEAPON = <ItemType.WEAPON: 3>
MESSAGE = <ItemType.MESSAGE: 7>
ENGRAM = <ItemType.ENGRAM: 8>
CONSUMABLE = <ItemType.CONSUMABLE: 9>
EXCHANGEMATERIAL = <ItemType.EXCHANGEMATERIAL: 10>
MISSIONREWARD = <ItemType.MISSIONREWARD: 11>
QUESTSTEP = <ItemType.QUESTSTEP: 12>
QUESTSTEPCOMPLETE = <ItemType.QUESTSTEPCOMPLETE: 13>
EMBLEM = <ItemType.EMBLEM: 14>
QUEST = <ItemType.QUEST: 15>
SUBCLASS = <ItemType.SUBCLASS: 16>
CLANBANNER = <ItemType.CLANBANNER: 17>
AURA = <ItemType.AURA: 18>
MOD = <ItemType.MOD: 19>
DUMMY = <ItemType.DUMMY: 20>
SHIP = <ItemType.SHIP: 21>
VEHICLE = <ItemType.VEHICLE: 22>
EMOTE = <ItemType.EMOTE: 23>
GHOST = <ItemType.GHOST: 24>
PACKAGE = <ItemType.PACKAGE: 25>
BOUNTY = <ItemType.BOUNTY: 26>
WRAPPER = <ItemType.WRAPPER: 27>
SEASONALARTIFACT = <ItemType.SEASONALARTIFACT: 28>
FINISHER = <ItemType.FINISHER: 29>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class Iterator(typing.Generic[~Item], collections.abc.Iterator[~Item]):
 47class Iterator(typing.Generic[Item], collections.Iterator[Item]):
 48    """A Flat, In-Memory iterator for sequenced based data.
 49
 50    Example
 51    -------
 52    ```py
 53    iterator = Iterator([1, 2, 3])
 54
 55    # Map the results.
 56    for item in iterator.map(lambda item: item * 2):
 57        print(item)
 58    # 2
 59    # 4
 60
 61    # Indexing is also supported.
 62    print(iterator[0])
 63    # 1
 64
 65    # Normal iteration.
 66    for item in iterator:
 67        print(item)
 68    # 1
 69    # 2
 70    # 3
 71
 72    # Union two iterators.
 73    iterator2 = Iterator([4, 5, 6])
 74    final = iterator | iterator2
 75    # <Iterator([1, 2, 3, 4, 5, 6])>
 76    ```
 77
 78    Parameters
 79    ----------
 80    items: `collections.Iterable[Item]`
 81        The items to iterate over.
 82    """
 83
 84    __slots__ = ("_items",)
 85
 86    def __init__(self, items: collections.Iterable[Item]) -> None:
 87        self._items = _builtins.iter(items)
 88
 89    @typing.overload
 90    def collect(self) -> collections.Sequence[Item]:
 91        ...
 92
 93    @typing.overload
 94    def collect(self, casting: _B) -> collections.Sequence[_B]:
 95        ...
 96
 97    def collect(
 98        self, casting: _B | None = None
 99    ) -> collections.Sequence[Item] | collections.Sequence[_B]:
100        """Collects all items in the iterator into an immutable collection.
101
102        Example
103        -------
104        >>> iterator = Iterator([1, 2, 3])
105        >>> iterator.collect(casting=str)
106        ("1", "2", "3")
107
108        Parameters
109        ----------
110        casting: `T | None`
111            The type to cast the items to. If `None` is provided, the items will be returned as is.
112
113        Returns
114        -------
115        `collections.Sequence[Item | T]`
116            An immutable sequence of the elements in the iterator.
117
118        Raises
119        ------
120        `StopIteration`
121            If no elements are left in the iterator.
122        """
123        if casting is not None:
124            return typing.cast(
125                collections.Sequence[_B], tuple(map(casting, self._items))
126            )
127
128        return tuple(self._items)
129
130    def copied(self) -> Iterator[Item]:
131        """Creates an iterator which `deeply` copies all of its elements.
132
133        .. warn::
134            This will `deeply` copy all of the elements, Use `Iterator.by_ref`
135            if you want copy of the iterator reference.
136
137        Example
138        -------
139        ```py
140        it = Iterator([None, None, None])
141        copied_iter = it.copied()
142        assert it.collect() == copied.collect()
143        ```
144        """
145        return Iterator(_copy.deepcopy(self._items))
146
147    def by_ref(self) -> Iterator[Item]:
148        """Creates an iterator which doesn't consume its elements.
149        but instead shallow copy it.
150
151        Example
152        -------
153        ```py
154        it = Iterator([None, None, None])
155        for ref in it.by_ref():
156            ...
157
158        # Original not consumed.
159        assert it.count() == 3
160        ```
161        """
162        return Iterator(_copy.copy(self._items))
163
164    def next(self) -> Item:
165        """Returns the next item in the iterator.
166
167        Example
168        -------
169        ```py
170        iterator = Iterator(["1", "2", "3"])
171        item = iterator.next()
172        assert item == "1"
173        item = iterator.next()
174        assert item == "2"
175        ```
176
177        Raises
178        ------
179        `StopIteration`
180            If no elements are left in the iterator.
181        """
182        try:
183            return self.__next__()
184        except StopIteration:
185            self._ok()
186
187    def map(
188        self, predicate: collections.Callable[[Item], OtherItem]
189    ) -> Iterator[OtherItem]:
190        """Maps each item in the iterator to its predicated value.
191
192        Example
193        -------
194        ```py
195        iterator = Iterator(["1", "2", "3"]).map(lambda value: int(value))
196        print(iterator)
197        # <Iterator([1, 2, 3])>
198        ```
199
200        Parameters
201        ----------
202        predicate: `collections.Callable[[Item], OtherItem]`
203            The function to map each item in the iterator to its predicated value.
204
205        Returns
206        -------
207        `Iterator[OtherItem]`
208            The mapped iterator.
209
210        Raises
211        ------
212        `StopIteration`
213            If no elements are left in the iterator.
214        """
215        return Iterator(map(predicate, self._items))
216
217    def take(self, n: int) -> Iterator[Item]:
218        """Take the first number of items until the number of items are yielded or
219        the end of the iterator is reached.
220
221        Example
222        -------
223        ```py
224        iterator = Iterator([GameMode.RAID, GameMode.STRIKE, GameMode.GAMBIT])
225        print(iterator.take(2))
226        # <Iterator([GameMode.RAID, GameMode.STRIKE])>
227        ```
228
229        Parameters
230        ----------
231        n: `int`
232            The number of items to take.
233
234        Raises
235        ------
236        `StopIteration`
237            If no elements are left in the iterator.
238        """
239        return Iterator(itertools.islice(self._items, n))
240
241    def take_while(
242        self, predicate: collections.Callable[[Item], bool]
243    ) -> Iterator[Item]:
244        """Yields items from the iterator while predicate returns `True`.
245
246        Example
247        -------
248        ```py
249        iterator = Iterator([STEAM, XBOX, STADIA])
250        print(iterator.take_while(lambda platform: platform is not XBOX))
251        # <Iterator([STEAM])>
252        ```
253
254        Parameters
255        ----------
256        predicate: `collections.Callable[[Item], bool]`
257            The function to predicate each item in the iterator.
258
259        Raises
260        ------
261        `StopIteration`
262            If no elements are left in the iterator.
263        """
264        return Iterator(itertools.takewhile(predicate, self._items))
265
266    def drop_while(
267        self, predicate: collections.Callable[[Item], bool]
268    ) -> Iterator[Item]:
269        """Yields items from the iterator while predicate returns `False`.
270
271        Example
272        -------
273        ```py
274        iterator = Iterator([DestinyMembership(name="Jim"), DestinyMembership(name="Bob")])
275        print(iterator.drop_while(lambda membership: membership.name is not "Jim"))
276        # <Iterator([DestinyMembership(name="Bob")])>
277        ```
278
279        Parameters
280        ----------
281        predicate: `collections.Callable[[Item], bool]`
282            The function to predicate each item in the iterator.
283
284        Raises
285        ------
286        `StopIteration`
287            If no elements are left in the iterator.
288        """
289        return Iterator(itertools.dropwhile(predicate, self._items))
290
291    def filter(self, predicate: collections.Callable[[Item], bool]) -> Iterator[Item]:
292        """Filters the iterator to only yield items that match the predicate.
293
294        Example
295        -------
296        ```py
297        names = Iterator(["Jim", "Bob", "Mike", "Jess"])
298        print(names.filter(lambda n: n != "Jim"))
299        # <Iterator(["Bob", "Mike", "Jess"])>
300        ```
301        """
302        return Iterator(filter(predicate, self._items))
303
304    def skip(self, n: int) -> Iterator[Item]:
305        """Skips the first number of items in the iterator.
306
307        Example
308        -------
309        ```py
310        iterator = Iterator([STEAM, XBOX, STADIA])
311        print(iterator.skip(1))
312        # <Iterator([XBOX, STADIA])>
313        ```
314        """
315        return Iterator(itertools.islice(self._items, n, None))
316
317    def zip(self, other: Iterator[OtherItem]) -> Iterator[tuple[Item, OtherItem]]:
318        """Zips the iterator with another iterable.
319
320        Example
321        -------
322        ```py
323        iterator = Iterator([1, 3, 5])
324        other = Iterator([2, 4, 6])
325        for item, other_item in iterator.zip(other):
326            print(item, other_item)
327        # <Iterator([(1, 2), (3, 4), (5, 6)])>
328        ```
329
330        Parameters
331        ----------
332        other: `Iterator[OtherItem]`
333            The iterable to zip with.
334
335        Raises
336        ------
337        `StopIteration`
338            If no elements are left in the iterator.
339        """
340        return Iterator(zip(self._items, other))
341
342    def all(self, predicate: collections.Callable[[Item], bool]) -> bool:
343        """`True` if all items in the iterator match the predicate.
344
345        Example
346        -------
347        ```py
348        iterator = Iterator([1, 2, 3])
349        while iterator.all(lambda item: isinstance(item, int)):
350            print("Still all integers")
351            continue
352        # Still all integers
353        ```
354
355        Parameters
356        ----------
357        predicate: `collections.Callable[[Item], bool]`
358            The function to test each item in the iterator.
359
360        Raises
361        ------
362        `StopIteration`
363            If no elements are left in the iterator.
364        """
365        return all(predicate(item) for item in self)
366
367    def any(self, predicate: collections.Callable[[Item], bool]) -> bool:
368        """`True` if any items in the iterator match the predicate.
369
370        Example
371        -------
372        ```py
373        iterator = Iterator([1, 2, 3])
374        if iterator.any(lambda item: isinstance(item, int)):
375            print("At least one item is an int.")
376        # At least one item is an int.
377        ```
378
379        Parameters
380        ----------
381        predicate: `collections.Callable[[Item], bool]`
382            The function to test each item in the iterator.
383
384        Raises
385        ------
386        `StopIteration`
387            If no elements are left in the iterator.
388        """
389        return any(predicate(item) for item in self)
390
391    def sort(
392        self,
393        *,
394        key: collections.Callable[[Item], typeshed.SupportsRichComparison],
395        reverse: bool = False,
396    ) -> Iterator[Item]:
397        """Sorts the iterator.
398
399        Example
400        -------
401        ```py
402        iterator = Iterator([3, 1, 6, 7])
403        print(iterator.sort(key=lambda item: item))
404        # <Iterator([1, 3, 6, 7])>
405        ```
406
407        Parameters
408        ----------
409        key: `collections.Callable[[Item], Any]`
410            The function to sort by.
411        reverse: `bool`
412            Whether to reverse the sort.
413
414        Raises
415        ------
416        `StopIteration`
417            If no elements are left in the iterator.
418        """
419        return Iterator(sorted(self._items, key=key, reverse=reverse))
420
421    def first(self) -> Item:
422        """Returns the first item in the iterator.
423
424        Example
425        -------
426        ```py
427        iterator = Iterator([3, 1, 6, 7])
428        print(iterator.first())
429        3
430        ```
431
432        Raises
433        ------
434        `StopIteration`
435            If no elements are left in the iterator.
436        """
437        return self.take(1).next()
438
439    def last(self) -> Item:
440        """Returns the last item in the iterator.
441
442        Example
443        ------
444        ```py
445        it = Iterator((1, 2, 3))
446        assert it.first() == 1 and it.last() == 3
447        ```
448        """
449        return self.reversed().first()
450
451    def reversed(self) -> Iterator[Item]:
452        """Returns a new iterator that yields the items in the iterator in reverse order.
453
454        Example
455        -------
456        ```py
457        iterator = Iterator([3, 1, 6, 7])
458        print(iterator.reversed())
459        # <Iterator([7, 6, 1, 3])>
460        ```
461
462        Raises
463        ------
464        `StopIteration`
465            If no elements are left in the iterator.
466        """
467        return Iterator(reversed(self.collect()))
468
469    def count(self) -> int:
470        """Returns the number of items in the iterator.
471
472        Example
473        -------
474        ```py
475        iterator = Iterator([3, 1, 6, 7])
476        print(iterator.count())
477        4
478        ```
479        """
480        count = 0
481        for _ in self:
482            count += 1
483
484        return count
485
486    def union(self, other: Iterator[Item]) -> Iterator[Item]:
487        """Returns a new iterator that yields all items from both iterators.
488
489        Example
490        -------
491        ```py
492        iterator = Iterator([1, 2, 3])
493        other = Iterator([4, 5, 6])
494        print(iterator.union(other))
495        # <Iterator([1, 2, 3, 4, 5, 6])>
496        ```
497
498        Parameters
499        ----------
500        other: `Iterator[Item]`
501            The iterable to union with.
502
503        Raises
504        ------
505        `StopIteration`
506            If no elements are left in the iterator.
507        """
508        return Iterator(itertools.chain(self._items, other))
509
510    def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None:
511        """Calls the function on each item in the iterator.
512
513        Example
514        -------
515        ```py
516        iterator = Iterator([1, 2, 3])
517        iterator.for_each(lambda item: print(item))
518        # 1
519        # 2
520        # 3
521        ```
522
523        Parameters
524        ----------
525        func: `typeshed.Callable[[Item], None]`
526            The function to call on each item in the iterator.
527        """
528        for item in self:
529            func(item)
530
531    async def async_for_each(
532        self,
533        func: collections.Callable[[Item], collections.Coroutine[None, None, None]],
534    ) -> None:
535        """Calls the async function on each item in the iterator concurrently.
536
537        Example
538        -------
539        ```py
540        async def signup(username: str) -> None:
541            async with aiohttp.request('POST', '...') as r:
542                # Actual logic.
543                ...
544
545        async def main():
546            users = aiobungie.into_iter(["user_danny", "user_jojo"])
547            await users.async_for_each(lambda username: signup(username))
548        ```
549
550        Parameters
551        ----------
552        func: `collections.Callable[[Item], collections.Coroutine[None, None, None]]`
553            The async function to call on each item in the iterator.
554        """
555        await _helpers.awaits(*(func(item) for item in self))
556
557    def enumerate(self, *, start: int = 0) -> Iterator[tuple[int, Item]]:
558        """Returns a new iterator that yields tuples of the index and item.
559
560        Example
561        -------
562        ```py
563        iterator = Iterator([1, 2, 3])
564        for index, item in iterator.enumerate():
565            print(index, item)
566        # 0 1
567        # 1 2
568        # 2 3
569        ```
570
571        Raises
572        ------
573        `StopIteration`
574            If no elements are left in the iterator.
575        """
576        return Iterator(enumerate(self._items, start=start))
577
578    def _ok(self) -> typing.NoReturn:
579        raise StopIteration("No more items in the iterator.") from None
580
581    def __getitem__(self, index: int) -> Item:
582        try:
583            return self.skip(index).first()
584        except IndexError:
585            self._ok()
586
587    def __or__(self, other: Iterator[Item]) -> Iterator[Item]:
588        return self.union(other)
589
590    # This is a never.
591    def __setitem__(self) -> typing.NoReturn:
592        raise TypeError(
593            f"{type(self).__name__} doesn't support item assignment."
594        ) from None
595
596    def __repr__(self) -> str:
597        return f"Iterator(ptr: {hex(id(self._items))})"
598
599    __str__ = __repr__
600
601    def __len__(self) -> int:
602        return self.count()
603
604    def __iter__(self) -> Iterator[Item]:
605        return self
606
607    def __next__(self) -> Item:
608        try:
609            item = next(self._items)
610        except StopIteration:
611            self._ok()
612
613        return item

A Flat, In-Memory iterator for sequenced based data.

Example
iterator = Iterator([1, 2, 3])

# Map the results.
for item in iterator.map(lambda item: item * 2):
    print(item)
# 2
# 4

# Indexing is also supported.
print(iterator[0])
# 1

# Normal iteration.
for item in iterator:
    print(item)
# 1
# 2
# 3

# Union two iterators.
iterator2 = Iterator([4, 5, 6])
final = iterator | iterator2
# <Iterator([1, 2, 3, 4, 5, 6])>
Parameters
  • items (collections.Iterable[Item]): The items to iterate over.
Iterator(items: collections.abc.Iterable[~Item])
86    def __init__(self, items: collections.Iterable[Item]) -> None:
87        self._items = _builtins.iter(items)
def collect( self, casting: '_B | None' = None) -> 'collections.Sequence[Item] | collections.Sequence[_B]':
 97    def collect(
 98        self, casting: _B | None = None
 99    ) -> collections.Sequence[Item] | collections.Sequence[_B]:
100        """Collects all items in the iterator into an immutable collection.
101
102        Example
103        -------
104        >>> iterator = Iterator([1, 2, 3])
105        >>> iterator.collect(casting=str)
106        ("1", "2", "3")
107
108        Parameters
109        ----------
110        casting: `T | None`
111            The type to cast the items to. If `None` is provided, the items will be returned as is.
112
113        Returns
114        -------
115        `collections.Sequence[Item | T]`
116            An immutable sequence of the elements in the iterator.
117
118        Raises
119        ------
120        `StopIteration`
121            If no elements are left in the iterator.
122        """
123        if casting is not None:
124            return typing.cast(
125                collections.Sequence[_B], tuple(map(casting, self._items))
126            )
127
128        return tuple(self._items)

Collects all items in the iterator into an immutable collection.

Example
>>> iterator = Iterator([1, 2, 3])
>>> iterator.collect(casting=str)
("1", "2", "3")
Parameters
  • casting (T | None): The type to cast the items to. If None is provided, the items will be returned as is.
Returns
  • collections.Sequence[Item | T]: An immutable sequence of the elements in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def copied(self) -> Iterator[~Item]:
130    def copied(self) -> Iterator[Item]:
131        """Creates an iterator which `deeply` copies all of its elements.
132
133        .. warn::
134            This will `deeply` copy all of the elements, Use `Iterator.by_ref`
135            if you want copy of the iterator reference.
136
137        Example
138        -------
139        ```py
140        it = Iterator([None, None, None])
141        copied_iter = it.copied()
142        assert it.collect() == copied.collect()
143        ```
144        """
145        return Iterator(_copy.deepcopy(self._items))

Creates an iterator which deeply copies all of its elements.

.. warn:: This will deeply copy all of the elements, Use Iterator.by_ref if you want copy of the iterator reference.

Example
it = Iterator([None, None, None])
copied_iter = it.copied()
assert it.collect() == copied.collect()
def by_ref(self) -> Iterator[~Item]:
147    def by_ref(self) -> Iterator[Item]:
148        """Creates an iterator which doesn't consume its elements.
149        but instead shallow copy it.
150
151        Example
152        -------
153        ```py
154        it = Iterator([None, None, None])
155        for ref in it.by_ref():
156            ...
157
158        # Original not consumed.
159        assert it.count() == 3
160        ```
161        """
162        return Iterator(_copy.copy(self._items))

Creates an iterator which doesn't consume its elements. but instead shallow copy it.

Example
it = Iterator([None, None, None])
for ref in it.by_ref():
    ...

# Original not consumed.
assert it.count() == 3
def next(self) -> ~Item:
164    def next(self) -> Item:
165        """Returns the next item in the iterator.
166
167        Example
168        -------
169        ```py
170        iterator = Iterator(["1", "2", "3"])
171        item = iterator.next()
172        assert item == "1"
173        item = iterator.next()
174        assert item == "2"
175        ```
176
177        Raises
178        ------
179        `StopIteration`
180            If no elements are left in the iterator.
181        """
182        try:
183            return self.__next__()
184        except StopIteration:
185            self._ok()

Returns the next item in the iterator.

Example
iterator = Iterator(["1", "2", "3"])
item = iterator.next()
assert item == "1"
item = iterator.next()
assert item == "2"
Raises
  • StopIteration: If no elements are left in the iterator.
def map( self, predicate: 'collections.Callable[[Item], OtherItem]') -> 'Iterator[OtherItem]':
187    def map(
188        self, predicate: collections.Callable[[Item], OtherItem]
189    ) -> Iterator[OtherItem]:
190        """Maps each item in the iterator to its predicated value.
191
192        Example
193        -------
194        ```py
195        iterator = Iterator(["1", "2", "3"]).map(lambda value: int(value))
196        print(iterator)
197        # <Iterator([1, 2, 3])>
198        ```
199
200        Parameters
201        ----------
202        predicate: `collections.Callable[[Item], OtherItem]`
203            The function to map each item in the iterator to its predicated value.
204
205        Returns
206        -------
207        `Iterator[OtherItem]`
208            The mapped iterator.
209
210        Raises
211        ------
212        `StopIteration`
213            If no elements are left in the iterator.
214        """
215        return Iterator(map(predicate, self._items))

Maps each item in the iterator to its predicated value.

Example
iterator = Iterator(["1", "2", "3"]).map(lambda value: int(value))
print(iterator)
# <Iterator([1, 2, 3])>
Parameters
  • predicate (collections.Callable[[Item], OtherItem]): The function to map each item in the iterator to its predicated value.
Returns
  • Iterator[OtherItem]: The mapped iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def take(self, n: int) -> Iterator[~Item]:
217    def take(self, n: int) -> Iterator[Item]:
218        """Take the first number of items until the number of items are yielded or
219        the end of the iterator is reached.
220
221        Example
222        -------
223        ```py
224        iterator = Iterator([GameMode.RAID, GameMode.STRIKE, GameMode.GAMBIT])
225        print(iterator.take(2))
226        # <Iterator([GameMode.RAID, GameMode.STRIKE])>
227        ```
228
229        Parameters
230        ----------
231        n: `int`
232            The number of items to take.
233
234        Raises
235        ------
236        `StopIteration`
237            If no elements are left in the iterator.
238        """
239        return Iterator(itertools.islice(self._items, n))

Take the first number of items until the number of items are yielded or the end of the iterator is reached.

Example
iterator = Iterator([GameMode.RAID, GameMode.STRIKE, GameMode.GAMBIT])
print(iterator.take(2))
# <Iterator([GameMode.RAID, GameMode.STRIKE])>
Parameters
  • n (int): The number of items to take.
Raises
  • StopIteration: If no elements are left in the iterator.
def take_while( self, predicate: collections.abc.Callable[[~Item], bool]) -> Iterator[~Item]:
241    def take_while(
242        self, predicate: collections.Callable[[Item], bool]
243    ) -> Iterator[Item]:
244        """Yields items from the iterator while predicate returns `True`.
245
246        Example
247        -------
248        ```py
249        iterator = Iterator([STEAM, XBOX, STADIA])
250        print(iterator.take_while(lambda platform: platform is not XBOX))
251        # <Iterator([STEAM])>
252        ```
253
254        Parameters
255        ----------
256        predicate: `collections.Callable[[Item], bool]`
257            The function to predicate each item in the iterator.
258
259        Raises
260        ------
261        `StopIteration`
262            If no elements are left in the iterator.
263        """
264        return Iterator(itertools.takewhile(predicate, self._items))

Yields items from the iterator while predicate returns True.

Example
iterator = Iterator([STEAM, XBOX, STADIA])
print(iterator.take_while(lambda platform: platform is not XBOX))
# <Iterator([STEAM])>
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to predicate each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def drop_while( self, predicate: collections.abc.Callable[[~Item], bool]) -> Iterator[~Item]:
266    def drop_while(
267        self, predicate: collections.Callable[[Item], bool]
268    ) -> Iterator[Item]:
269        """Yields items from the iterator while predicate returns `False`.
270
271        Example
272        -------
273        ```py
274        iterator = Iterator([DestinyMembership(name="Jim"), DestinyMembership(name="Bob")])
275        print(iterator.drop_while(lambda membership: membership.name is not "Jim"))
276        # <Iterator([DestinyMembership(name="Bob")])>
277        ```
278
279        Parameters
280        ----------
281        predicate: `collections.Callable[[Item], bool]`
282            The function to predicate each item in the iterator.
283
284        Raises
285        ------
286        `StopIteration`
287            If no elements are left in the iterator.
288        """
289        return Iterator(itertools.dropwhile(predicate, self._items))

Yields items from the iterator while predicate returns False.

Example
iterator = Iterator([DestinyMembership(name="Jim"), DestinyMembership(name="Bob")])
print(iterator.drop_while(lambda membership: membership.name is not "Jim"))
# <Iterator([DestinyMembership(name="Bob")])>
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to predicate each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def filter( self, predicate: collections.abc.Callable[[~Item], bool]) -> Iterator[~Item]:
291    def filter(self, predicate: collections.Callable[[Item], bool]) -> Iterator[Item]:
292        """Filters the iterator to only yield items that match the predicate.
293
294        Example
295        -------
296        ```py
297        names = Iterator(["Jim", "Bob", "Mike", "Jess"])
298        print(names.filter(lambda n: n != "Jim"))
299        # <Iterator(["Bob", "Mike", "Jess"])>
300        ```
301        """
302        return Iterator(filter(predicate, self._items))

Filters the iterator to only yield items that match the predicate.

Example
names = Iterator(["Jim", "Bob", "Mike", "Jess"])
print(names.filter(lambda n: n != "Jim"))
# <Iterator(["Bob", "Mike", "Jess"])>
def skip(self, n: int) -> Iterator[~Item]:
304    def skip(self, n: int) -> Iterator[Item]:
305        """Skips the first number of items in the iterator.
306
307        Example
308        -------
309        ```py
310        iterator = Iterator([STEAM, XBOX, STADIA])
311        print(iterator.skip(1))
312        # <Iterator([XBOX, STADIA])>
313        ```
314        """
315        return Iterator(itertools.islice(self._items, n, None))

Skips the first number of items in the iterator.

Example
iterator = Iterator([STEAM, XBOX, STADIA])
print(iterator.skip(1))
# <Iterator([XBOX, STADIA])>
def zip(self, other: 'Iterator[OtherItem]') -> 'Iterator[tuple[Item, OtherItem]]':
317    def zip(self, other: Iterator[OtherItem]) -> Iterator[tuple[Item, OtherItem]]:
318        """Zips the iterator with another iterable.
319
320        Example
321        -------
322        ```py
323        iterator = Iterator([1, 3, 5])
324        other = Iterator([2, 4, 6])
325        for item, other_item in iterator.zip(other):
326            print(item, other_item)
327        # <Iterator([(1, 2), (3, 4), (5, 6)])>
328        ```
329
330        Parameters
331        ----------
332        other: `Iterator[OtherItem]`
333            The iterable to zip with.
334
335        Raises
336        ------
337        `StopIteration`
338            If no elements are left in the iterator.
339        """
340        return Iterator(zip(self._items, other))

Zips the iterator with another iterable.

Example
iterator = Iterator([1, 3, 5])
other = Iterator([2, 4, 6])
for item, other_item in iterator.zip(other):
    print(item, other_item)
# <Iterator([(1, 2), (3, 4), (5, 6)])>
Parameters
  • other (Iterator[OtherItem]): The iterable to zip with.
Raises
  • StopIteration: If no elements are left in the iterator.
def all(self, predicate: collections.abc.Callable[[~Item], bool]) -> bool:
342    def all(self, predicate: collections.Callable[[Item], bool]) -> bool:
343        """`True` if all items in the iterator match the predicate.
344
345        Example
346        -------
347        ```py
348        iterator = Iterator([1, 2, 3])
349        while iterator.all(lambda item: isinstance(item, int)):
350            print("Still all integers")
351            continue
352        # Still all integers
353        ```
354
355        Parameters
356        ----------
357        predicate: `collections.Callable[[Item], bool]`
358            The function to test each item in the iterator.
359
360        Raises
361        ------
362        `StopIteration`
363            If no elements are left in the iterator.
364        """
365        return all(predicate(item) for item in self)

True if all items in the iterator match the predicate.

Example
iterator = Iterator([1, 2, 3])
while iterator.all(lambda item: isinstance(item, int)):
    print("Still all integers")
    continue
# Still all integers
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to test each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def any(self, predicate: collections.abc.Callable[[~Item], bool]) -> bool:
367    def any(self, predicate: collections.Callable[[Item], bool]) -> bool:
368        """`True` if any items in the iterator match the predicate.
369
370        Example
371        -------
372        ```py
373        iterator = Iterator([1, 2, 3])
374        if iterator.any(lambda item: isinstance(item, int)):
375            print("At least one item is an int.")
376        # At least one item is an int.
377        ```
378
379        Parameters
380        ----------
381        predicate: `collections.Callable[[Item], bool]`
382            The function to test each item in the iterator.
383
384        Raises
385        ------
386        `StopIteration`
387            If no elements are left in the iterator.
388        """
389        return any(predicate(item) for item in self)

True if any items in the iterator match the predicate.

Example
iterator = Iterator([1, 2, 3])
if iterator.any(lambda item: isinstance(item, int)):
    print("At least one item is an int.")
# At least one item is an int.
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to test each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def sort( self, *, key: 'collections.Callable[[Item], typeshed.SupportsRichComparison]', reverse: bool = False) -> Iterator[~Item]:
391    def sort(
392        self,
393        *,
394        key: collections.Callable[[Item], typeshed.SupportsRichComparison],
395        reverse: bool = False,
396    ) -> Iterator[Item]:
397        """Sorts the iterator.
398
399        Example
400        -------
401        ```py
402        iterator = Iterator([3, 1, 6, 7])
403        print(iterator.sort(key=lambda item: item))
404        # <Iterator([1, 3, 6, 7])>
405        ```
406
407        Parameters
408        ----------
409        key: `collections.Callable[[Item], Any]`
410            The function to sort by.
411        reverse: `bool`
412            Whether to reverse the sort.
413
414        Raises
415        ------
416        `StopIteration`
417            If no elements are left in the iterator.
418        """
419        return Iterator(sorted(self._items, key=key, reverse=reverse))

Sorts the iterator.

Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.sort(key=lambda item: item))
# <Iterator([1, 3, 6, 7])>
Parameters
  • key (collections.Callable[[Item], Any]): The function to sort by.
  • reverse (bool): Whether to reverse the sort.
Raises
  • StopIteration: If no elements are left in the iterator.
def first(self) -> ~Item:
421    def first(self) -> Item:
422        """Returns the first item in the iterator.
423
424        Example
425        -------
426        ```py
427        iterator = Iterator([3, 1, 6, 7])
428        print(iterator.first())
429        3
430        ```
431
432        Raises
433        ------
434        `StopIteration`
435            If no elements are left in the iterator.
436        """
437        return self.take(1).next()

Returns the first item in the iterator.

Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.first())
3
Raises
  • StopIteration: If no elements are left in the iterator.
def last(self) -> ~Item:
439    def last(self) -> Item:
440        """Returns the last item in the iterator.
441
442        Example
443        ------
444        ```py
445        it = Iterator((1, 2, 3))
446        assert it.first() == 1 and it.last() == 3
447        ```
448        """
449        return self.reversed().first()

Returns the last item in the iterator.

Example
it = Iterator((1, 2, 3))
assert it.first() == 1 and it.last() == 3
def reversed(self) -> Iterator[~Item]:
451    def reversed(self) -> Iterator[Item]:
452        """Returns a new iterator that yields the items in the iterator in reverse order.
453
454        Example
455        -------
456        ```py
457        iterator = Iterator([3, 1, 6, 7])
458        print(iterator.reversed())
459        # <Iterator([7, 6, 1, 3])>
460        ```
461
462        Raises
463        ------
464        `StopIteration`
465            If no elements are left in the iterator.
466        """
467        return Iterator(reversed(self.collect()))

Returns a new iterator that yields the items in the iterator in reverse order.

Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.reversed())
# <Iterator([7, 6, 1, 3])>
Raises
  • StopIteration: If no elements are left in the iterator.
def count(self) -> int:
469    def count(self) -> int:
470        """Returns the number of items in the iterator.
471
472        Example
473        -------
474        ```py
475        iterator = Iterator([3, 1, 6, 7])
476        print(iterator.count())
477        4
478        ```
479        """
480        count = 0
481        for _ in self:
482            count += 1
483
484        return count

Returns the number of items in the iterator.

Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.count())
4
def union( self, other: Iterator[~Item]) -> Iterator[~Item]:
486    def union(self, other: Iterator[Item]) -> Iterator[Item]:
487        """Returns a new iterator that yields all items from both iterators.
488
489        Example
490        -------
491        ```py
492        iterator = Iterator([1, 2, 3])
493        other = Iterator([4, 5, 6])
494        print(iterator.union(other))
495        # <Iterator([1, 2, 3, 4, 5, 6])>
496        ```
497
498        Parameters
499        ----------
500        other: `Iterator[Item]`
501            The iterable to union with.
502
503        Raises
504        ------
505        `StopIteration`
506            If no elements are left in the iterator.
507        """
508        return Iterator(itertools.chain(self._items, other))

Returns a new iterator that yields all items from both iterators.

Example
iterator = Iterator([1, 2, 3])
other = Iterator([4, 5, 6])
print(iterator.union(other))
# <Iterator([1, 2, 3, 4, 5, 6])>
Parameters
  • other (Iterator[Item]): The iterable to union with.
Raises
  • StopIteration: If no elements are left in the iterator.
def for_each(self, func: collections.abc.Callable[[~Item], typing.Any]) -> None:
510    def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None:
511        """Calls the function on each item in the iterator.
512
513        Example
514        -------
515        ```py
516        iterator = Iterator([1, 2, 3])
517        iterator.for_each(lambda item: print(item))
518        # 1
519        # 2
520        # 3
521        ```
522
523        Parameters
524        ----------
525        func: `typeshed.Callable[[Item], None]`
526            The function to call on each item in the iterator.
527        """
528        for item in self:
529            func(item)

Calls the function on each item in the iterator.

Example
iterator = Iterator([1, 2, 3])
iterator.for_each(lambda item: print(item))
# 1
# 2
# 3
Parameters
  • func (typeshed.Callable[[Item], None]): The function to call on each item in the iterator.
async def async_for_each( self, func: collections.abc.Callable[[~Item], collections.abc.Coroutine[None, None, None]]) -> None:
531    async def async_for_each(
532        self,
533        func: collections.Callable[[Item], collections.Coroutine[None, None, None]],
534    ) -> None:
535        """Calls the async function on each item in the iterator concurrently.
536
537        Example
538        -------
539        ```py
540        async def signup(username: str) -> None:
541            async with aiohttp.request('POST', '...') as r:
542                # Actual logic.
543                ...
544
545        async def main():
546            users = aiobungie.into_iter(["user_danny", "user_jojo"])
547            await users.async_for_each(lambda username: signup(username))
548        ```
549
550        Parameters
551        ----------
552        func: `collections.Callable[[Item], collections.Coroutine[None, None, None]]`
553            The async function to call on each item in the iterator.
554        """
555        await _helpers.awaits(*(func(item) for item in self))

Calls the async function on each item in the iterator concurrently.

Example
async def signup(username: str) -> None:
    async with aiohttp.request('POST', '...') as r:
        # Actual logic.
        ...

async def main():
    users = aiobungie.into_iter(["user_danny", "user_jojo"])
    await users.async_for_each(lambda username: signup(username))
Parameters
  • func (collections.Callable[[Item], collections.Coroutine[None, None, None]]): The async function to call on each item in the iterator.
def enumerate( self, *, start: int = 0) -> Iterator[tuple[int, ~Item]]:
557    def enumerate(self, *, start: int = 0) -> Iterator[tuple[int, Item]]:
558        """Returns a new iterator that yields tuples of the index and item.
559
560        Example
561        -------
562        ```py
563        iterator = Iterator([1, 2, 3])
564        for index, item in iterator.enumerate():
565            print(index, item)
566        # 0 1
567        # 1 2
568        # 2 3
569        ```
570
571        Raises
572        ------
573        `StopIteration`
574            If no elements are left in the iterator.
575        """
576        return Iterator(enumerate(self._items, start=start))

Returns a new iterator that yields tuples of the index and item.

Example
iterator = Iterator([1, 2, 3])
for index, item in iterator.enumerate():
    print(index, item)
# 0 1
# 1 2
# 2 3
Raises
  • StopIteration: If no elements are left in the iterator.
@typing.final
class MembershipOption(builtins.int, aiobungie.Enum):
702@typing.final
703class MembershipOption(int, Enum):
704    """A enum for GroupV2 membership options."""
705
706    REVIEWD = 0
707    OPEN = 1
708    CLOSED = 2

A enum for GroupV2 membership options.

REVIEWD = <MembershipOption.REVIEWD: 0>
OPEN = <MembershipOption.OPEN: 1>
CLOSED = <MembershipOption.CLOSED: 2>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class MembershipType(builtins.int, aiobungie.Enum):
450@typing.final
451class MembershipType(int, Enum):
452    """An Enum for Bungie membership types."""
453
454    NONE = 0
455    XBOX = 1
456    PSN = 2
457    STEAM = 3
458    BLIZZARD = 4
459    STADIA = 5
460    EPIC_GAMES_STORE = 6
461    DEMON = 10
462    BUNGIE = 254
463    ALL = -1

An Enum for Bungie membership types.

NONE = <MembershipType.NONE: 0>
XBOX = <MembershipType.XBOX: 1>
PSN = <MembershipType.PSN: 2>
STEAM = <MembershipType.STEAM: 3>
BLIZZARD = <MembershipType.BLIZZARD: 4>
STADIA = <MembershipType.STADIA: 5>
EPIC_GAMES_STORE = <MembershipType.EPIC_GAMES_STORE: 6>
DEMON = <MembershipType.DEMON: 10>
BUNGIE = <MembershipType.BUNGIE: 254>
ALL = <MembershipType.ALL: -1>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class MembershipTypeError(aiobungie.BadRequest):
181@attrs.define(auto_exc=True)
182class MembershipTypeError(BadRequest):
183    """A bad request error raised when passing wrong membership to the request.
184
185    Those fields are useful since it returns the correct membership and id which can be used
186    to make the request again with those fields.
187
188    Example
189    -------
190    ```py
191    try:
192        profile = await client.fetch_profile(
193            member_id=1,
194            type=aiobungie.MembershipType.STADIA,
195            components=()
196        )
197
198    # Membership type is wrong!
199    except aiobungie.MembershipTypeError as err:
200        correct_membership = err.into_membership()
201        profile_id = err.membership_id
202
203        # Recall the method.
204        profile = await client.fetch_profile(
205            member_id=profile_id,
206            type=correct_membership,
207            components=()
208        )
209    ```
210    """
211
212    membership_type: str
213    """The errored membership type passed to the request."""
214
215    membership_id: int
216    """The errored user's membership id."""
217
218    required_membership: str
219    """The required correct membership for errored user."""
220
221    def into_membership(self, value: str | None = None) -> enums.MembershipType:
222        """Turn the required membership from `str` into `aiobungie.Membership` type.
223
224        If value parameter is not provided it will fall back to the required membership.
225        """
226        if value is None:
227            return _MEMBERSHIP_LOOKUP[self.required_membership]
228        return _MEMBERSHIP_LOOKUP[value]
229
230    def __str__(self) -> str:
231        return (
232            f"Expected membership: {self.into_membership().name.replace('_', '').title()}, "
233            f"But got {self.into_membership(self.membership_type)} for id {self.membership_id}"
234        )
235
236    def __int__(self) -> int:
237        return int(self.membership_id)

A bad request error raised when passing wrong membership to the request.

Those fields are useful since it returns the correct membership and id which can be used to make the request again with those fields.

Example
try:
    profile = await client.fetch_profile(
        member_id=1,
        type=aiobungie.MembershipType.STADIA,
        components=()
    )

# Membership type is wrong!
except aiobungie.MembershipTypeError as err:
    correct_membership = err.into_membership()
    profile_id = err.membership_id

    # Recall the method.
    profile = await client.fetch_profile(
        member_id=profile_id,
        type=correct_membership,
        components=()
    )
MembershipTypeError( message: str, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], membership_type: str, membership_id: int, required_membership: str)
 2def __init__(self, message, url, body, headers, membership_type, membership_id, required_membership):
 3    self.message = message
 4    self.url = url
 5    self.body = body
 6    self.headers = headers
 7    self.http_status = attr_dict['http_status'].default
 8    self.membership_type = membership_type
 9    self.membership_id = membership_id
10    self.required_membership = required_membership
11    BaseException.__init__(self, self.message,self.url,self.body,self.headers,self.membership_type,self.membership_id,self.required_membership)

Method generated by attrs for class MembershipTypeError.

membership_type: str

The errored membership type passed to the request.

membership_id: int

The errored user's membership id.

required_membership: str

The required correct membership for errored user.

def into_membership( self, value: str | None = None) -> MembershipType:
221    def into_membership(self, value: str | None = None) -> enums.MembershipType:
222        """Turn the required membership from `str` into `aiobungie.Membership` type.
223
224        If value parameter is not provided it will fall back to the required membership.
225        """
226        if value is None:
227            return _MEMBERSHIP_LOOKUP[self.required_membership]
228        return _MEMBERSHIP_LOOKUP[value]

Turn the required membership from str into aiobungie.Membership type.

If value parameter is not provided it will fall back to the required membership.

Inherited Members
BadRequest
url
body
headers
http_status
HTTPError
message
builtins.BaseException
with_traceback
args
@typing.final
class MilestoneType(builtins.int, aiobungie.Enum):
495@typing.final
496class MilestoneType(int, Enum):
497    """An Enum for Destiny 2 milestone types."""
498
499    UNKNOWN = 0
500    TUTORIAL = 1
501    ONETIME = 2
502    WEEKLY = 3
503    DAILY = 4
504    SPECIAL = 5

An Enum for Destiny 2 milestone types.

UNKNOWN = <MilestoneType.UNKNOWN: 0>
TUTORIAL = <MilestoneType.TUTORIAL: 1>
ONETIME = <MilestoneType.ONETIME: 2>
WEEKLY = <MilestoneType.WEEKLY: 3>
DAILY = <MilestoneType.DAILY: 4>
SPECIAL = <MilestoneType.SPECIAL: 5>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class NotFound(aiobungie.HTTPException):
145@attrs.define(auto_exc=True)
146class NotFound(HTTPException):
147    """Raised when an unknown resource was not found."""
148
149    http_status: http.HTTPStatus = attrs.field(
150        default=http.HTTPStatus.NOT_FOUND, init=False
151    )

Raised when an unknown resource was not found.

NotFound( *, error_code: int, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.throttle_seconds = throttle_seconds
 5    self.url = url
 6    self.body = body
 7    self.headers = headers
 8    self.message = message
 9    self.error_status = error_status
10    self.message_data = message_data
11    self.http_status = attr_dict['http_status'].default
12    BaseException.__init__(self, self.error_code,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class NotFound.

http_status: http.HTTPStatus

The request response http status.

Inherited Members
HTTPException
error_code
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
args
@typing.final
class ObjectiveUIStyle(builtins.int, aiobungie.Enum):
 93@typing.final
 94class ObjectiveUIStyle(int, enums.Enum):
 95    NONE = 0
 96    HIGHLIGHTED = 1
 97    CRAFTING_WEAPON_LEVEL = 2
 98    CRAFTING_WEAPON_LEVEL_PROGRESS = 3
 99    CRAFTING_WEAPON_TIMESTAMP = 4
100    CRAFTING_MEMENTOS = 5
101    CRAFTING_MEMENTO_TITLE = 6

An enumeration.

NONE = <ObjectiveUIStyle.NONE: 0>
HIGHLIGHTED = <ObjectiveUIStyle.HIGHLIGHTED: 1>
CRAFTING_WEAPON_LEVEL = <ObjectiveUIStyle.CRAFTING_WEAPON_LEVEL: 2>
CRAFTING_WEAPON_LEVEL_PROGRESS = <ObjectiveUIStyle.CRAFTING_WEAPON_LEVEL_PROGRESS: 3>
CRAFTING_WEAPON_TIMESTAMP = <ObjectiveUIStyle.CRAFTING_WEAPON_TIMESTAMP: 4>
CRAFTING_MEMENTOS = <ObjectiveUIStyle.CRAFTING_MEMENTOS: 5>
CRAFTING_MEMENTO_TITLE = <ObjectiveUIStyle.CRAFTING_MEMENTO_TITLE: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Place(builtins.int, aiobungie.Enum):
219@typing.final
220class Place(int, Enum):
221    """An Enum for Destiny 2 Places and NOT Planets"""
222
223    ORBIT = 2961497387
224    SOCIAL = 4151112093
225    LIGHT_HOUSE = 4276116472
226    EXPLORE = 3497767639

An Enum for Destiny 2 Places and NOT Planets

ORBIT = <Place.ORBIT: 2961497387>
SOCIAL = <Place.SOCIAL: 4151112093>
LIGHT_HOUSE = <Place.LIGHT_HOUSE: 4276116472>
EXPLORE = <Place.EXPLORE: 3497767639>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Planet(builtins.int, aiobungie.Enum):
184@typing.final
185class Planet(int, Enum):
186    """An Enum for all available planets in Destiny 2."""
187
188    UNKNOWN = 0
189    """Unknown space"""
190
191    EARTH = 3747705955
192    """Earth"""
193
194    DREAMING_CITY = 2877881518
195    """The Dreaming city."""
196
197    NESSUS = 3526908984
198    """Nessus"""
199
200    MOON = 3325508439
201    """The Moon"""
202
203    COSMODROME = 3990611421
204    """The Cosmodrome"""
205
206    TANGLED_SHORE = 3821439926
207    """The Tangled Shore"""
208
209    VENUS = 3871070152
210    """Venus"""
211
212    EAZ = 541863059  # Exclusive event.
213    """European Aerial Zone"""
214
215    EUROPA = 1729879943
216    """Europa"""

An Enum for all available planets in Destiny 2.

UNKNOWN = <Planet.UNKNOWN: 0>

Unknown space

EARTH = <Planet.EARTH: 3747705955>

Earth

DREAMING_CITY = <Planet.DREAMING_CITY: 2877881518>

The Dreaming city.

NESSUS = <Planet.NESSUS: 3526908984>

Nessus

MOON = <Planet.MOON: 3325508439>

The Moon

COSMODROME = <Planet.COSMODROME: 3990611421>

The Cosmodrome

TANGLED_SHORE = <Planet.TANGLED_SHORE: 3821439926>

The Tangled Shore

VENUS = <Planet.VENUS: 3871070152>

Venus

EAZ = <Planet.EAZ: 541863059>

European Aerial Zone

EUROPA = <Planet.EUROPA: 1729879943>

Europa

Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Presence(builtins.int, aiobungie.Enum):
672@typing.final
673class Presence(int, Enum):
674    """An enum for a bungie friend status."""
675
676    OFFLINE_OR_UNKNOWN = 0
677    ONLINE = 1

An enum for a bungie friend status.

OFFLINE_OR_UNKNOWN = <Presence.OFFLINE_OR_UNKNOWN: 0>
ONLINE = <Presence.ONLINE: 1>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class PrivacySetting(builtins.int, aiobungie.Enum):
760@typing.final
761class PrivacySetting(int, Enum):
762    """An enum for players's privacy settings."""
763
764    OPEN = 0
765    CLAN_AND_FRIENDS = 1
766    FRIENDS_ONLY = 2
767    INVITE_ONLY = 3
768    CLOSED = 4

An enum for players's privacy settings.

OPEN = <PrivacySetting.OPEN: 0>
CLAN_AND_FRIENDS = <PrivacySetting.CLAN_AND_FRIENDS: 1>
FRIENDS_ONLY = <PrivacySetting.FRIENDS_ONLY: 2>
INVITE_ONLY = <PrivacySetting.INVITE_ONLY: 3>
CLOSED = <PrivacySetting.CLOSED: 4>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class RESTClient(aiobungie.interfaces.rest.RESTInterface):
 309class RESTClient(interfaces.RESTInterface):
 310    """A RESTful client implementation for Bungie's API.
 311
 312    This client is designed to only make HTTP requests and return JSON objects
 313    to provide RESTful functionality.
 314
 315    This client is the core for `aiobungie.Client` which deserialize those returned JSON objects
 316    using the factory into Pythonic data classes objects which provide Python functionality.
 317
 318    Example
 319    -------
 320    ```py
 321    import aiobungie
 322
 323    client = aiobungie.RESTClient("TOKEN")
 324    async with client:
 325        response = await client.fetch_clan_members(4389205)
 326        for member in response['results']:
 327            for key, value in member['destinyUserInfo'].items():
 328                print(key, value)
 329    ```
 330
 331    Parameters
 332    ----------
 333    token : `str`
 334        A valid application token from Bungie's developer portal.
 335
 336    Other Parameters
 337    ----------------
 338    max_retries : `int`
 339        The max retries number to retry if the request hit a `5xx` status code.
 340    client_secret : `str | None`
 341        An optional application client secret,
 342        This is only needed if you're fetching OAuth2 tokens with this client.
 343    client_id : `int | None`
 344        An optional application client id,
 345        This is only needed if you're fetching OAuth2 tokens with this client.
 346    debug : `bool | str`
 347        Whether to enable logging responses or not.
 348
 349    Logging Levels
 350    --------------
 351    * `False`: This will disable logging.
 352    * `True`: This will set the level to `DEBUG` and enable logging minimal information.
 353    * `"TRACE" | aiobungie.TRACE`: This will log the response headers along with the minimal information.
 354    """
 355
 356    __slots__ = (
 357        "_token",
 358        "_session",
 359        "_lock",
 360        "_max_retries",
 361        "_client_secret",
 362        "_client_id",
 363        "_metadata",
 364        "_dumps",
 365        "_print",
 366        "_loads",
 367    )
 368
 369    def __init__(
 370        self,
 371        token: str,
 372        /,
 373        *,
 374        client_secret: str | None = None,
 375        client_id: int | None = None,
 376        client_session: aiohttp.ClientSession | None = None,
 377        dumps: typedefs.Dumps = helpers.dumps,
 378        loads: typedefs.Loads = helpers.loads,
 379        max_retries: int = 4,
 380        debug: typing.Literal["TRACE"] | bool | int = False,
 381    ) -> None:
 382        self._session = client_session
 383        self._lock: asyncio.Lock | None = None
 384        self._client_secret = client_secret
 385        self._client_id = client_id
 386        self._token: str = token
 387        self._max_retries = max_retries
 388        self._dumps = dumps
 389        self._loads = loads
 390        self._print = False
 391        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
 392        self.with_debug(debug)
 393
 394    @property
 395    def client_id(self) -> int | None:
 396        return self._client_id
 397
 398    @property
 399    def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]:
 400        return self._metadata
 401
 402    @property
 403    def is_alive(self) -> bool:
 404        return self._session is not None
 405
 406    @typing.final
 407    async def close(self) -> None:
 408        if self._session is None:
 409            raise RuntimeError("REST client is not running.")
 410
 411        await self._session.close()
 412        self._session = None
 413
 414    @typing.final
 415    def open(self) -> None:
 416        """Open a new client session. This is called internally with contextmanager usage."""
 417        if self._session:
 418            raise RuntimeError("Cannot open REST client when it's already open.")
 419
 420        self._session = aiohttp.ClientSession(
 421            connector=aiohttp.TCPConnector(),
 422            connector_owner=True,
 423            raise_for_status=False,
 424            timeout=aiohttp.ClientTimeout(total=30.0),
 425        )
 426
 427    @typing.final
 428    async def static_request(
 429        self,
 430        method: _HTTP_METHOD,
 431        path: str,
 432        *,
 433        auth: str | None = None,
 434        json: collections.Mapping[str, typing.Any] | None = None,
 435    ) -> typedefs.JSONIsh:
 436        return await self._request(method, path, auth=auth, json=json)
 437
 438    @typing.final
 439    def build_oauth2_url(
 440        self, client_id: int | None = None
 441    ) -> builders.OAuthURL | None:
 442        client_id = client_id or self._client_id
 443        if client_id is None:
 444            return None
 445
 446        return builders.OAuthURL(client_id=client_id)
 447
 448    @typing.final
 449    def with_debug(
 450        self,
 451        level: typing.Literal["TRACE"] | bool | int = True,
 452        file: str | pathlib.Path | None = None,
 453    ) -> None:
 454        """Enable debugging for this client with a level. Defaults to `True`.
 455
 456        Parameters
 457        ----------
 458        level: `NotRequired[int | bool | typing.Literal["TRACE"] | None]`
 459            The level of the logger. This field is not required.
 460        file: `pathlib.Path | str | None`
 461            An optional file to write the logs into.
 462
 463        Logging Levels
 464        --------------
 465        * `False`: This will disable logging.
 466        * `True`: This will set the level to `DEBUG` and enable logging minimal information.
 467        * `"TRACE" | aiobungie.TRACE`: This will log the response headers along with the minimal information.
 468        """
 469        logging.logThreads = False
 470        logging.logMultiprocessing = False
 471        logging.logProcesses = False
 472        logging.captureWarnings(True)
 473
 474        format = "%(levelname)s " "%(asctime)23.23s " "%(name)s: " "%(message)s"
 475
 476        file_handler = (logging.FileHandler(file),) if file else None
 477        if level == "TRACE" or level == TRACE:
 478            logging.basicConfig(
 479                level=TRACE, format=format, stream=sys.stdout, handlers=file_handler
 480            )
 481
 482        elif level is True:
 483            logging.basicConfig(
 484                level=logging.DEBUG,
 485                format=format,
 486                stream=sys.stdout,
 487                handlers=file_handler,
 488            )
 489
 490    async def _request(
 491        self,
 492        method: _HTTP_METHOD,
 493        route: str,
 494        *,
 495        base: bool = False,
 496        oauth2: bool = False,
 497        auth: str | None = None,
 498        unwrap_bytes: bool = False,
 499        json: collections.Mapping[str, typing.Any] | None = None,
 500        data: collections.Mapping[str, typing.Any] | None = None,
 501        params: collections.Mapping[str, typing.Any] | None = None,
 502    ) -> typedefs.JSONIsh:
 503        # This is not None when opening the client.
 504        assert self._session is not None
 505
 506        retries: int = 0
 507        headers: collections.MutableMapping[str, typing.Any] = {}
 508
 509        headers[_USER_AGENT_HEADERS] = _USER_AGENT
 510        headers["X-API-KEY"] = self._token
 511
 512        if auth is not None:
 513            headers[_AUTH_HEADER] = f"Bearer {auth}"
 514
 515        # Handling endpoints
 516        endpoint = url.BASE
 517
 518        if not base:
 519            endpoint = endpoint + url.REST_EP
 520
 521        if oauth2:
 522            assert self._client_id
 523            assert self._client_secret
 524            headers["client_secret"] = self._client_secret
 525
 526            headers["Content-Type"] = "application/x-www-form-urlencoded"
 527            endpoint = endpoint + url.TOKEN_EP
 528
 529        if self._lock is None:
 530            self._lock = asyncio.Lock()
 531
 532        if json:
 533            headers["Content-Type"] = _APP_JSON
 534
 535        stack = contextlib.AsyncExitStack()
 536        while True:
 537            try:
 538                await stack.enter_async_context(self._lock)
 539
 540                # We make the request here.
 541                taken_time = time.monotonic()
 542                response = await self._session.request(
 543                    method=method,
 544                    url=f"{endpoint}/{route}",
 545                    headers=headers,
 546                    data=_JSONPayload(json) if json else data,
 547                    params=params,
 548                )
 549                response_time = (time.monotonic() - taken_time) * 1_000
 550
 551                _LOGGER.debug(
 552                    "%s %s %s Time %.4fms",
 553                    method,
 554                    f"{endpoint}/{route}",
 555                    f"{response.status} {response.reason}",
 556                    response_time,
 557                )
 558
 559                await self._handle_ratelimit(response, method, route)
 560
 561            except aiohttp.ClientConnectionError as exc:
 562                if retries >= self._max_retries:
 563                    raise error.HTTPError(
 564                        str(exc),
 565                        http.HTTPStatus.SERVICE_UNAVAILABLE,
 566                    )
 567                backoff_ = backoff.ExponentialBackOff(maximum=8)
 568
 569                timer = next(backoff_)
 570                _LOGGER.warning(
 571                    "Client received a connection error <%s> Retrying in %.2fs. Remaining retries: %s",
 572                    type(exc).__qualname__,
 573                    timer,
 574                    self._max_retries - retries,
 575                )
 576                retries += 1
 577                await asyncio.sleep(timer)
 578                continue
 579
 580            finally:
 581                await stack.aclose()
 582
 583            if response.status == http.HTTPStatus.NO_CONTENT:
 584                return None
 585
 586            # Handle the successful response.
 587            if 300 > response.status >= 200:
 588                if unwrap_bytes:
 589                    # We need to read the bytes for the manifest response.
 590                    return await response.read()
 591
 592                # Bungie get funky and return HTML instead of JSON when making an authorized
 593                # request with a dummy access token. We could technically read the page content
 594                # but that's Bungie's fault for not returning a JSON response.
 595                if response.content_type != _APP_JSON:
 596                    raise error.HTTPError(
 597                        message=f"Expected JSON response, Got {response.content_type}, "
 598                        f"{response.real_url.human_repr()}",
 599                        http_status=http.HTTPStatus(response.status),
 600                    )
 601
 602                json_data = self._loads(await response.read())
 603
 604                _LOGGER.debug(
 605                    "%s %s %s Time %.4fms",
 606                    method,
 607                    f"{endpoint}/{route}",
 608                    f"{response.status} {response.reason}",
 609                    response_time,
 610                )
 611
 612                if _LOGGER.isEnabledFor(TRACE):
 613                    _LOGGER.log(
 614                        TRACE,
 615                        "%s",
 616                        error.stringify_headers(dict(response.headers)),
 617                    )
 618
 619                    details: collections.MutableMapping[str, typing.Any] = {}
 620                    if json:
 621                        details["json"] = json
 622
 623                    if data:
 624                        details["data"] = data
 625
 626                    if params:
 627                        details["params"] = params
 628
 629                    if details:
 630                        _LOGGER.log(TRACE, "%s", error.stringify_headers(details))
 631
 632                # Return the response.
 633                # auth responses are not inside a Response object.
 634                if oauth2:
 635                    return json_data
 636
 637                # The reason we have a type ignore is because the actual response type
 638                # is within this `Response` key.
 639                return json_data["Response"]  # type: ignore
 640
 641            if (
 642                response.status in _RETRY_5XX and retries < self._max_retries  # noqa: W503
 643            ):
 644                backoff_ = backoff.ExponentialBackOff(maximum=6)
 645                sleep_time = next(backoff_)
 646                _LOGGER.warning(
 647                    "Got %i - %s. Sleeping for %.2f seconds. Remaining retries: %i",
 648                    response.status,
 649                    response.reason,
 650                    sleep_time,
 651                    self._max_retries - retries,
 652                )
 653
 654                retries += 1
 655                await asyncio.sleep(sleep_time)
 656                continue
 657
 658            raise await error.panic(response)
 659
 660    async def __aenter__(self) -> RESTClient:
 661        self.open()
 662        return self
 663
 664    async def __aexit__(
 665        self,
 666        exception_type: type[BaseException] | None,
 667        exception: BaseException | None,
 668        exception_traceback: types.TracebackType | None,
 669    ) -> None:
 670        await self.close()
 671
 672    # We don't want this to be super complicated.
 673    @typing.final
 674    async def _handle_ratelimit(
 675        self,
 676        response: aiohttp.ClientResponse,
 677        method: str,
 678        route: str,
 679    ) -> None:
 680        if response.status != http.HTTPStatus.TOO_MANY_REQUESTS:
 681            return
 682
 683        if response.content_type != _APP_JSON:
 684            raise error.HTTPError(
 685                f"Being ratelimited on non JSON request, {response.content_type}.",
 686                http.HTTPStatus.TOO_MANY_REQUESTS,
 687            )
 688
 689        # The reason we have a type ignore here is that we guaranteed the content type is JSON above.
 690        json: typedefs.JSONObject = self._loads(await response.read())  # type: ignore
 691        retry_after = float(json.get("ThrottleSeconds", 15.0)) + 0.1
 692        max_calls: int = 0
 693
 694        while True:
 695            if max_calls == 10:
 696                # Max retries by default. We raise an error here.
 697                raise error.RateLimitedError(
 698                    body=json,
 699                    url=str(response.real_url),
 700                    retry_after=retry_after,
 701                )
 702
 703            # We sleep for a little bit to avoid funky behavior.
 704            _LOGGER.warning(
 705                "We're being ratelimited, Method %s Route %s. Sleeping for %.2fs.",
 706                method,
 707                route,
 708                retry_after,
 709            )
 710            await asyncio.sleep(retry_after)
 711            max_calls += 1
 712            continue
 713
 714    async def fetch_oauth2_tokens(self, code: str, /) -> builders.OAuth2Response:
 715        data = {
 716            "grant_type": "authorization_code",
 717            "code": code,
 718            "client_id": self._client_id,
 719            "client_secret": self._client_secret,
 720        }
 721
 722        response = await self._request(_POST, "", data=data, oauth2=True)
 723        assert isinstance(response, dict)
 724        return builders.OAuth2Response.build_response(response)
 725
 726    async def refresh_access_token(
 727        self, refresh_token: str, /
 728    ) -> builders.OAuth2Response:
 729        data = {
 730            "grant_type": "refresh_token",
 731            "refresh_token": refresh_token,
 732            "client_id": self._client_id,
 733            "client_secret": self._client_secret,
 734        }
 735
 736        response = await self._request(_POST, "", data=data, oauth2=True)
 737        assert isinstance(response, dict)
 738        return builders.OAuth2Response.build_response(response)
 739
 740    async def fetch_bungie_user(self, id: int) -> typedefs.JSONObject:
 741        resp = await self._request(_GET, f"User/GetBungieNetUserById/{id}/")
 742        assert isinstance(resp, dict)
 743        return resp
 744
 745    async def fetch_user_themes(self) -> typedefs.JSONArray:
 746        resp = await self._request(_GET, "User/GetAvailableThemes/")
 747        assert isinstance(resp, list)
 748        return resp
 749
 750    async def fetch_membership_from_id(
 751        self,
 752        id: int,
 753        type: enums.MembershipType | int = enums.MembershipType.NONE,
 754        /,
 755    ) -> typedefs.JSONObject:
 756        resp = await self._request(_GET, f"User/GetMembershipsById/{id}/{int(type)}")
 757        assert isinstance(resp, dict)
 758        return resp
 759
 760    async def fetch_membership(
 761        self,
 762        name: str,
 763        code: int,
 764        type: enums.MembershipType | int = enums.MembershipType.ALL,
 765        /,
 766    ) -> typedefs.JSONArray:
 767        resp = await self._request(
 768            _POST,
 769            f"Destiny2/SearchDestinyPlayerByBungieName/{int(type)}",
 770            json={"displayName": name, "displayNameCode": code},
 771        )
 772        assert isinstance(resp, list)
 773        return resp
 774
 775    async def search_users(self, name: str, /) -> typedefs.JSONObject:
 776        resp = await self._request(
 777            _POST,
 778            "User/Search/GlobalName/0",
 779            json={"displayNamePrefix": name},
 780        )
 781        assert isinstance(resp, dict)
 782        return resp
 783
 784    async def fetch_clan_from_id(
 785        self, id: int, /, access_token: str | None = None
 786    ) -> typedefs.JSONObject:
 787        resp = await self._request(_GET, f"GroupV2/{id}", auth=access_token)
 788        assert isinstance(resp, dict)
 789        return resp
 790
 791    async def fetch_clan(
 792        self,
 793        name: str,
 794        /,
 795        access_token: str | None = None,
 796        *,
 797        type: enums.GroupType | int = enums.GroupType.CLAN,
 798    ) -> typedefs.JSONObject:
 799        resp = await self._request(
 800            _GET, f"GroupV2/Name/{name}/{int(type)}", auth=access_token
 801        )
 802        assert isinstance(resp, dict)
 803        return resp
 804
 805    async def fetch_clan_admins(self, clan_id: int, /) -> typedefs.JSONObject:
 806        resp = await self._request(_GET, f"GroupV2/{clan_id}/AdminsAndFounder/")
 807        assert isinstance(resp, dict)
 808        return resp
 809
 810    async def fetch_clan_conversations(self, clan_id: int, /) -> typedefs.JSONArray:
 811        resp = await self._request(_GET, f"GroupV2/{clan_id}/OptionalConversations/")
 812        assert isinstance(resp, list)
 813        return resp
 814
 815    async def fetch_application(self, appid: int, /) -> typedefs.JSONObject:
 816        resp = await self._request(_GET, f"App/Application/{appid}")
 817        assert isinstance(resp, dict)
 818        return resp
 819
 820    async def fetch_character(
 821        self,
 822        member_id: int,
 823        membership_type: enums.MembershipType | int,
 824        character_id: int,
 825        components: collections.Sequence[enums.ComponentType],
 826        auth: str | None = None,
 827    ) -> typedefs.JSONObject:
 828        collector = _collect_components(components)
 829        response = await self._request(
 830            _GET,
 831            f"Destiny2/{int(membership_type)}/Profile/{member_id}/"
 832            f"Character/{character_id}/?components={collector}",
 833            auth=auth,
 834        )
 835        assert isinstance(response, dict)
 836        return response
 837
 838    async def fetch_activities(
 839        self,
 840        member_id: int,
 841        character_id: int,
 842        mode: enums.GameMode | int,
 843        membership_type: enums.MembershipType | int = enums.MembershipType.ALL,
 844        *,
 845        page: int = 0,
 846        limit: int = 1,
 847    ) -> typedefs.JSONObject:
 848        resp = await self._request(
 849            _GET,
 850            f"Destiny2/{int(membership_type)}/Account/"
 851            f"{member_id}/Character/{character_id}/Stats/Activities"
 852            f"/?mode={int(mode)}&count={limit}&page={page}",
 853        )
 854        assert isinstance(resp, dict)
 855        return resp
 856
 857    async def fetch_vendor_sales(self) -> typedefs.JSONObject:
 858        resp = await self._request(
 859            _GET,
 860            f"Destiny2/Vendors/?components={int(enums.ComponentType.VENDOR_SALES)}",
 861        )
 862        assert isinstance(resp, dict)
 863        return resp
 864
 865    async def fetch_profile(
 866        self,
 867        membership_id: int,
 868        type: enums.MembershipType | int,
 869        components: collections.Sequence[enums.ComponentType],
 870        auth: str | None = None,
 871    ) -> typedefs.JSONObject:
 872        collector = _collect_components(components)
 873        response = await self._request(
 874            _GET,
 875            f"Destiny2/{int(type)}/Profile/{membership_id}/?components={collector}",
 876            auth=auth,
 877        )
 878        assert isinstance(response, dict)
 879        return response
 880
 881    async def fetch_entity(self, type: str, hash: int) -> typedefs.JSONObject:
 882        response = await self._request(_GET, route=f"Destiny2/Manifest/{type}/{hash}")
 883        assert isinstance(response, dict)
 884        return response
 885
 886    async def fetch_inventory_item(self, hash: int, /) -> typedefs.JSONObject:
 887        resp = await self.fetch_entity("DestinyInventoryItemDefinition", hash)
 888        assert isinstance(resp, dict)
 889        return resp
 890
 891    async def fetch_objective_entity(self, hash: int, /) -> typedefs.JSONObject:
 892        resp = await self.fetch_entity("DestinyObjectiveDefinition", hash)
 893        assert isinstance(resp, dict)
 894        return resp
 895
 896    async def fetch_groups_for_member(
 897        self,
 898        member_id: int,
 899        member_type: enums.MembershipType | int,
 900        /,
 901        *,
 902        filter: int = 0,
 903        group_type: enums.GroupType | int = enums.GroupType.CLAN,
 904    ) -> typedefs.JSONObject:
 905        resp = await self._request(
 906            _GET,
 907            f"GroupV2/User/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
 908        )
 909        assert isinstance(resp, dict)
 910        return resp
 911
 912    async def fetch_potential_groups_for_member(
 913        self,
 914        member_id: int,
 915        member_type: enums.MembershipType | int,
 916        /,
 917        *,
 918        filter: int = 0,
 919        group_type: enums.GroupType | int = enums.GroupType.CLAN,
 920    ) -> typedefs.JSONObject:
 921        resp = await self._request(
 922            _GET,
 923            f"GroupV2/User/Potential/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
 924        )
 925        assert isinstance(resp, dict)
 926        return resp
 927
 928    async def fetch_clan_members(
 929        self,
 930        clan_id: int,
 931        /,
 932        *,
 933        name: str | None = None,
 934        type: enums.MembershipType | int = enums.MembershipType.NONE,
 935    ) -> typedefs.JSONObject:
 936        resp = await self._request(
 937            _GET,
 938            f"/GroupV2/{clan_id}/Members/?memberType={int(type)}&nameSearch={name if name else ''}&currentpage=1",
 939        )
 940        assert isinstance(resp, dict)
 941        return resp
 942
 943    async def fetch_hardlinked_credentials(
 944        self,
 945        credential: int,
 946        type: enums.CredentialType | int = enums.CredentialType.STEAMID,
 947        /,
 948    ) -> typedefs.JSONObject:
 949        resp = await self._request(
 950            _GET,
 951            f"User/GetMembershipFromHardLinkedCredential/{int(type)}/{credential}/",
 952        )
 953        assert isinstance(resp, dict)
 954        return resp
 955
 956    async def fetch_user_credentials(
 957        self, access_token: str, membership_id: int, /
 958    ) -> typedefs.JSONArray:
 959        resp = await self._request(
 960            _GET,
 961            f"User/GetCredentialTypesForTargetAccount/{membership_id}",
 962            auth=access_token,
 963        )
 964        assert isinstance(resp, list)
 965        return resp
 966
 967    async def insert_socket_plug(
 968        self,
 969        action_token: str,
 970        /,
 971        instance_id: int,
 972        plug: builders.PlugSocketBuilder | collections.Mapping[str, int],
 973        character_id: int,
 974        membership_type: enums.MembershipType | int,
 975    ) -> typedefs.JSONObject:
 976        if isinstance(plug, builders.PlugSocketBuilder):
 977            plug = plug.collect()
 978
 979        body = {
 980            "actionToken": action_token,
 981            "itemInstanceId": instance_id,
 982            "plug": plug,
 983            "characterId": character_id,
 984            "membershipType": int(membership_type),
 985        }
 986        resp = await self._request(
 987            _POST, "Destiny2/Actions/Items/InsertSocketPlug", json=body
 988        )
 989        assert isinstance(resp, dict)
 990        return resp
 991
 992    async def insert_socket_plug_free(
 993        self,
 994        access_token: str,
 995        /,
 996        instance_id: int,
 997        plug: builders.PlugSocketBuilder | collections.Mapping[str, int],
 998        character_id: int,
 999        membership_type: enums.MembershipType | int,
1000    ) -> typedefs.JSONObject:
1001        if isinstance(plug, builders.PlugSocketBuilder):
1002            plug = plug.collect()
1003
1004        body = {
1005            "itemInstanceId": instance_id,
1006            "plug": plug,
1007            "characterId": character_id,
1008            "membershipType": int(membership_type),
1009        }
1010        resp = await self._request(
1011            _POST,
1012            "Destiny2/Actions/Items/InsertSocketPlugFree",
1013            json=body,
1014            auth=access_token,
1015        )
1016        assert isinstance(resp, dict)
1017        return resp
1018
1019    @helpers.unstable
1020    async def set_item_lock_state(
1021        self,
1022        access_token: str,
1023        state: bool,
1024        /,
1025        item_id: int,
1026        character_id: int,
1027        membership_type: enums.MembershipType | int,
1028    ) -> int:
1029        self._print = True
1030        body = {
1031            "state": state,
1032            "itemId": item_id,
1033            "characterId": character_id,
1034            "membershipType": int(membership_type),
1035        }
1036        response = await self._request(
1037            _POST,
1038            "Destiny2/Actions/Items/SetLockState",
1039            json=body,
1040            auth=access_token,
1041        )
1042        assert isinstance(response, int)
1043        return response
1044
1045    async def set_quest_track_state(
1046        self,
1047        access_token: str,
1048        state: bool,
1049        /,
1050        item_id: int,
1051        character_id: int,
1052        membership_type: enums.MembershipType | int,
1053    ) -> int:
1054        body = {
1055            "state": state,
1056            "itemId": item_id,
1057            "characterId": character_id,
1058            "membership_type": int(membership_type),
1059        }
1060        response = await self._request(
1061            _POST,
1062            "Destiny2/Actions/Items/SetTrackedState",
1063            json=body,
1064            auth=access_token,
1065        )
1066        assert isinstance(response, int)
1067        return response
1068
1069    async def fetch_manifest_path(self) -> typedefs.JSONObject:
1070        path = await self._request(_GET, "Destiny2/Manifest")
1071        assert isinstance(path, dict)
1072        return path
1073
1074    async def read_manifest_bytes(self, language: str = "en", /) -> bytes:
1075        _ensure_manifest_language(language)
1076
1077        content = await self.fetch_manifest_path()
1078        resp = await self._request(
1079            _GET,
1080            content["mobileWorldContentPaths"][language],
1081            unwrap_bytes=True,
1082            base=True,
1083        )
1084        assert isinstance(resp, bytes)
1085        return resp
1086
1087    async def download_sqlite_manifest(
1088        self,
1089        language: str = "en",
1090        name: str = "manifest",
1091        path: pathlib.Path | str = ".",
1092        *,
1093        force: bool = False,
1094    ) -> pathlib.Path:
1095        complete_path = _get_path(name, path, sql=True)
1096
1097        if complete_path.exists() and force:
1098            if force:
1099                _LOGGER.info(
1100                    f"Found manifest in {complete_path!s}. Forcing to Re-Download."
1101                )
1102                complete_path.unlink(missing_ok=True)
1103
1104                return await self.download_sqlite_manifest(
1105                    language, name, path, force=force
1106                )
1107
1108            else:
1109                raise FileExistsError(
1110                    "Manifest file already exists, "
1111                    "To force download, set the `force` parameter to `True`."
1112                )
1113
1114        _LOGGER.info(f"Downloading manifest. Location: {complete_path!s}")
1115        data_bytes = await self.read_manifest_bytes(language)
1116        await asyncio.get_running_loop().run_in_executor(
1117            None, _write_sqlite_bytes, data_bytes, path, name
1118        )
1119        _LOGGER.info("Finished downloading manifest.")
1120        return _get_path(name, path, sql=True)
1121
1122    async def download_json_manifest(
1123        self,
1124        file_name: str = "manifest",
1125        path: str | pathlib.Path = ".",
1126        language: str = "en",
1127    ) -> pathlib.Path:
1128        _ensure_manifest_language(language)
1129        #
1130        full_path = _get_path(file_name, path)
1131        _LOGGER.info(f"Downloading manifest JSON to {full_path!r}...")
1132
1133        content = await self.fetch_manifest_path()
1134        json_bytes = await self._request(
1135            _GET,
1136            content["jsonWorldContentPaths"][language],
1137            unwrap_bytes=True,
1138            base=True,
1139        )
1140
1141        assert isinstance(json_bytes, bytes)
1142        await asyncio.get_running_loop().run_in_executor(
1143            None, _write_json_bytes, json_bytes, file_name, path
1144        )
1145        _LOGGER.info("Finished downloading manifest JSON.")
1146        return full_path
1147
1148    async def fetch_manifest_version(self) -> str:
1149        # This is guaranteed str.
1150        return (await self.fetch_manifest_path())["version"]
1151
1152    async def fetch_linked_profiles(
1153        self,
1154        member_id: int,
1155        member_type: enums.MembershipType | int,
1156        /,
1157        *,
1158        all: bool = False,
1159    ) -> typedefs.JSONObject:
1160        resp = await self._request(
1161            _GET,
1162            f"Destiny2/{int(member_type)}/Profile/{member_id}/LinkedProfiles/?getAllMemberships={all}",
1163        )
1164        assert isinstance(resp, dict)
1165        return resp
1166
1167    async def fetch_clan_banners(self) -> typedefs.JSONObject:
1168        resp = await self._request(_GET, "Destiny2/Clan/ClanBannerDictionary/")
1169        assert isinstance(resp, dict)
1170        return resp
1171
1172    async def fetch_public_milestones(self) -> typedefs.JSONObject:
1173        resp = await self._request(_GET, "Destiny2/Milestones/")
1174        assert isinstance(resp, dict)
1175        return resp
1176
1177    async def fetch_public_milestone_content(
1178        self, milestone_hash: int, /
1179    ) -> typedefs.JSONObject:
1180        resp = await self._request(
1181            _GET, f"Destiny2/Milestones/{milestone_hash}/Content/"
1182        )
1183        assert isinstance(resp, dict)
1184        return resp
1185
1186    async def fetch_current_user_memberships(
1187        self, access_token: str, /
1188    ) -> typedefs.JSONObject:
1189        resp = await self._request(
1190            _GET,
1191            "User/GetMembershipsForCurrentUser/",
1192            auth=access_token,
1193        )
1194        assert isinstance(resp, dict)
1195        return resp
1196
1197    async def equip_item(
1198        self,
1199        access_token: str,
1200        /,
1201        item_id: int,
1202        character_id: int,
1203        membership_type: enums.MembershipType | int,
1204    ) -> None:
1205        payload = {
1206            "itemId": item_id,
1207            "characterId": character_id,
1208            "membershipType": int(membership_type),
1209        }
1210
1211        await self._request(
1212            _POST,
1213            "Destiny2/Actions/Items/EquipItem/",
1214            json=payload,
1215            auth=access_token,
1216        )
1217
1218    async def equip_items(
1219        self,
1220        access_token: str,
1221        /,
1222        item_ids: collections.Sequence[int],
1223        character_id: int,
1224        membership_type: enums.MembershipType | int,
1225    ) -> None:
1226        payload = {
1227            "itemIds": item_ids,
1228            "characterId": character_id,
1229            "membershipType": int(membership_type),
1230        }
1231        await self._request(
1232            _POST,
1233            "Destiny2/Actions/Items/EquipItems/",
1234            json=payload,
1235            auth=access_token,
1236        )
1237
1238    async def ban_clan_member(
1239        self,
1240        access_token: str,
1241        /,
1242        group_id: int,
1243        membership_id: int,
1244        membership_type: enums.MembershipType | int,
1245        *,
1246        length: int = 0,
1247        comment: str | None = None,
1248    ) -> None:
1249        payload = {"comment": str(comment), "length": length}
1250        await self._request(
1251            _POST,
1252            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Ban/",
1253            json=payload,
1254            auth=access_token,
1255        )
1256
1257    async def unban_clan_member(
1258        self,
1259        access_token: str,
1260        /,
1261        group_id: int,
1262        membership_id: int,
1263        membership_type: enums.MembershipType | int,
1264    ) -> None:
1265        await self._request(
1266            _POST,
1267            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Unban/",
1268            auth=access_token,
1269        )
1270
1271    async def kick_clan_member(
1272        self,
1273        access_token: str,
1274        /,
1275        group_id: int,
1276        membership_id: int,
1277        membership_type: enums.MembershipType | int,
1278    ) -> typedefs.JSONObject:
1279        resp = await self._request(
1280            _POST,
1281            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Kick/",
1282            auth=access_token,
1283        )
1284        assert isinstance(resp, dict)
1285        return resp
1286
1287    async def edit_clan(
1288        self,
1289        access_token: str,
1290        /,
1291        group_id: int,
1292        *,
1293        name: str | None = None,
1294        about: str | None = None,
1295        motto: str | None = None,
1296        theme: str | None = None,
1297        tags: collections.Sequence[str] | None = None,
1298        is_public: bool | None = None,
1299        locale: str | None = None,
1300        avatar_image_index: int | None = None,
1301        membership_option: enums.MembershipOption | int | None = None,
1302        allow_chat: bool | None = None,
1303        chat_security: typing.Literal[0, 1] | None = None,
1304        call_sign: str | None = None,
1305        homepage: typing.Literal[0, 1, 2] | None = None,
1306        enable_invite_messaging_for_admins: bool | None = None,
1307        default_publicity: typing.Literal[0, 1, 2] | None = None,
1308        is_public_topic_admin: bool | None = None,
1309    ) -> None:
1310        payload = {
1311            "name": name,
1312            "about": about,
1313            "motto": motto,
1314            "theme": theme,
1315            "tags": tags,
1316            "isPublic": is_public,
1317            "avatarImageIndex": avatar_image_index,
1318            "isPublicTopicAdminOnly": is_public_topic_admin,
1319            "allowChat": allow_chat,
1320            "chatSecurity": chat_security,
1321            "callsign": call_sign,
1322            "homepage": homepage,
1323            "enableInvitationMessagingForAdmins": enable_invite_messaging_for_admins,
1324            "defaultPublicity": default_publicity,
1325            "locale": locale,
1326        }
1327        if membership_option is not None:
1328            payload["membershipOption"] = int(membership_option)
1329
1330        await self._request(
1331            _POST,
1332            f"GroupV2/{group_id}/Edit",
1333            json=payload,
1334            auth=access_token,
1335        )
1336
1337    async def edit_clan_options(
1338        self,
1339        access_token: str,
1340        /,
1341        group_id: int,
1342        *,
1343        invite_permissions_override: bool | None = None,
1344        update_culture_permissionOverride: bool | None = None,
1345        host_guided_game_permission_override: typing.Literal[0, 1, 2] | None = None,
1346        update_banner_permission_override: bool | None = None,
1347        join_level: enums.ClanMemberType | int | None = None,
1348    ) -> None:
1349        payload = {
1350            "InvitePermissionOverride": invite_permissions_override,
1351            "UpdateCulturePermissionOverride": update_culture_permissionOverride,
1352            "HostGuidedGamePermissionOverride": host_guided_game_permission_override,
1353            "UpdateBannerPermissionOverride": update_banner_permission_override,
1354            "JoinLevel": int(join_level) if join_level else None,
1355        }
1356
1357        await self._request(
1358            _POST,
1359            f"GroupV2/{group_id}/EditFounderOptions",
1360            json=payload,
1361            auth=access_token,
1362        )
1363
1364    async def fetch_friends(self, access_token: str, /) -> typedefs.JSONObject:
1365        resp = await self._request(
1366            _GET,
1367            "Social/Friends/",
1368            auth=access_token,
1369        )
1370        assert isinstance(resp, dict)
1371        return resp
1372
1373    async def fetch_friend_requests(self, access_token: str, /) -> typedefs.JSONObject:
1374        resp = await self._request(
1375            _GET,
1376            "Social/Friends/Requests",
1377            auth=access_token,
1378        )
1379        assert isinstance(resp, dict)
1380        return resp
1381
1382    async def accept_friend_request(self, access_token: str, /, member_id: int) -> None:
1383        await self._request(
1384            _POST,
1385            f"Social/Friends/Requests/Accept/{member_id}",
1386            auth=access_token,
1387        )
1388
1389    async def send_friend_request(self, access_token: str, /, member_id: int) -> None:
1390        await self._request(
1391            _POST,
1392            f"Social/Friends/Add/{member_id}",
1393            auth=access_token,
1394        )
1395
1396    async def decline_friend_request(
1397        self, access_token: str, /, member_id: int
1398    ) -> None:
1399        await self._request(
1400            _POST,
1401            f"Social/Friends/Requests/Decline/{member_id}",
1402            auth=access_token,
1403        )
1404
1405    async def remove_friend(self, access_token: str, /, member_id: int) -> None:
1406        await self._request(
1407            _POST,
1408            f"Social/Friends/Remove/{member_id}",
1409            auth=access_token,
1410        )
1411
1412    async def remove_friend_request(self, access_token: str, /, member_id: int) -> None:
1413        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1414        await self._request(
1415            _POST,
1416            f"Social/Friends/Requests/Remove/{member_id}",
1417            auth=access_token,
1418        )
1419
1420    async def approve_all_pending_group_users(
1421        self,
1422        access_token: str,
1423        /,
1424        group_id: int,
1425        message: str | None = None,
1426    ) -> None:
1427        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1428        await self._request(
1429            _POST,
1430            f"GroupV2/{group_id}/Members/ApproveAll",
1431            auth=access_token,
1432            json={"message": str(message)},
1433        )
1434
1435    async def deny_all_pending_group_users(
1436        self,
1437        access_token: str,
1438        /,
1439        group_id: int,
1440        *,
1441        message: str | None = None,
1442    ) -> None:
1443        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1444        await self._request(
1445            _POST,
1446            f"GroupV2/{group_id}/Members/DenyAll",
1447            auth=access_token,
1448            json={"message": str(message)},
1449        )
1450
1451    async def add_optional_conversation(
1452        self,
1453        access_token: str,
1454        /,
1455        group_id: int,
1456        *,
1457        name: str | None = None,
1458        security: typing.Literal[0, 1] = 0,
1459    ) -> None:
1460        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1461        payload = {"chatName": str(name), "chatSecurity": security}
1462        await self._request(
1463            _POST,
1464            f"GroupV2/{group_id}/OptionalConversations/Add",
1465            json=payload,
1466            auth=access_token,
1467        )
1468
1469    async def edit_optional_conversation(
1470        self,
1471        access_token: str,
1472        /,
1473        group_id: int,
1474        conversation_id: int,
1475        *,
1476        name: str | None = None,
1477        security: typing.Literal[0, 1] = 0,
1478        enable_chat: bool = False,
1479    ) -> None:
1480        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1481        payload = {
1482            "chatEnabled": enable_chat,
1483            "chatName": str(name),
1484            "chatSecurity": security,
1485        }
1486        await self._request(
1487            _POST,
1488            f"GroupV2/{group_id}/OptionalConversations/Edit/{conversation_id}",
1489            json=payload,
1490            auth=access_token,
1491        )
1492
1493    async def transfer_item(
1494        self,
1495        access_token: str,
1496        /,
1497        item_id: int,
1498        item_hash: int,
1499        character_id: int,
1500        member_type: enums.MembershipType | int,
1501        *,
1502        stack_size: int = 1,
1503        vault: bool = False,
1504    ) -> None:
1505        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1506        payload = {
1507            "characterId": character_id,
1508            "membershipType": int(member_type),
1509            "itemId": item_id,
1510            "itemReferenceHash": item_hash,
1511            "stackSize": stack_size,
1512            "transferToVault": vault,
1513        }
1514        await self._request(
1515            _POST,
1516            "Destiny2/Actions/Items/TransferItem",
1517            json=payload,
1518            auth=access_token,
1519        )
1520
1521    async def pull_item(
1522        self,
1523        access_token: str,
1524        /,
1525        item_id: int,
1526        item_hash: int,
1527        character_id: int,
1528        member_type: enums.MembershipType | int,
1529        *,
1530        stack_size: int = 1,
1531        vault: bool = False,
1532    ) -> None:
1533        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1534        payload = {
1535            "characterId": character_id,
1536            "membershipType": int(member_type),
1537            "itemId": item_id,
1538            "itemReferenceHash": item_hash,
1539            "stackSize": stack_size,
1540            "transferToVault": vault,
1541        }
1542        await self._request(
1543            _POST,
1544            "Destiny2/Actions/Items/PullFromPostmaster",
1545            json=payload,
1546            auth=access_token,
1547        )
1548
1549    async def fetch_fireteams(
1550        self,
1551        activity_type: fireteams.FireteamActivity | int,
1552        *,
1553        platform: fireteams.FireteamPlatform | int = fireteams.FireteamPlatform.ANY,
1554        language: fireteams.FireteamLanguage | str = fireteams.FireteamLanguage.ALL,
1555        date_range: fireteams.FireteamDate | int = fireteams.FireteamDate.ALL,
1556        page: int = 0,
1557        slots_filter: int = 0,
1558    ) -> typedefs.JSONObject:
1559        resp = await self._request(
1560            _GET,
1561            f"Fireteam/Search/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{page}/?langFilter={str(language)}",  # noqa: E501 Line too long
1562        )
1563        assert isinstance(resp, dict)
1564        return resp
1565
1566    async def fetch_available_clan_fireteams(
1567        self,
1568        access_token: str,
1569        group_id: int,
1570        activity_type: fireteams.FireteamActivity | int,
1571        *,
1572        platform: fireteams.FireteamPlatform | int,
1573        language: fireteams.FireteamLanguage | str,
1574        date_range: fireteams.FireteamDate | int = fireteams.FireteamDate.ALL,
1575        page: int = 0,
1576        public_only: bool = False,
1577        slots_filter: int = 0,
1578    ) -> typedefs.JSONObject:
1579        resp = await self._request(
1580            _GET,
1581            f"Fireteam/Clan/{group_id}/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{public_only}/{page}",  # noqa: E501
1582            json={"langFilter": str(language)},
1583            auth=access_token,
1584        )
1585        assert isinstance(resp, dict)
1586        return resp
1587
1588    async def fetch_clan_fireteam(
1589        self, access_token: str, fireteam_id: int, group_id: int
1590    ) -> typedefs.JSONObject:
1591        resp = await self._request(
1592            _GET,
1593            f"Fireteam/Clan/{group_id}/Summary/{fireteam_id}",
1594            auth=access_token,
1595        )
1596        assert isinstance(resp, dict)
1597        return resp
1598
1599    async def fetch_my_clan_fireteams(
1600        self,
1601        access_token: str,
1602        group_id: int,
1603        *,
1604        include_closed: bool = True,
1605        platform: fireteams.FireteamPlatform | int,
1606        language: fireteams.FireteamLanguage | str,
1607        filtered: bool = True,
1608        page: int = 0,
1609    ) -> typedefs.JSONObject:
1610        payload = {"groupFilter": filtered, "langFilter": str(language)}
1611
1612        resp = await self._request(
1613            _GET,
1614            f"Fireteam/Clan/{group_id}/My/{int(platform)}/{include_closed}/{page}",
1615            json=payload,
1616            auth=access_token,
1617        )
1618        assert isinstance(resp, dict)
1619        return resp
1620
1621    async def fetch_private_clan_fireteams(
1622        self, access_token: str, group_id: int, /
1623    ) -> int:
1624        resp = await self._request(
1625            _GET,
1626            f"Fireteam/Clan/{group_id}/ActiveCount",
1627            auth=access_token,
1628        )
1629        assert isinstance(resp, int)
1630        return resp
1631
1632    async def fetch_post_activity(self, instance_id: int, /) -> typedefs.JSONObject:
1633        resp = await self._request(
1634            _GET, f"Destiny2/Stats/PostGameCarnageReport/{instance_id}"
1635        )
1636        assert isinstance(resp, dict)
1637        return resp
1638
1639    async def search_entities(
1640        self, name: str, entity_type: str, *, page: int = 0
1641    ) -> typedefs.JSONObject:
1642        resp = await self._request(
1643            _GET,
1644            f"Destiny2/Armory/Search/{entity_type}/{name}/",
1645            json={"page": page},
1646        )
1647        assert isinstance(resp, dict)
1648        return resp
1649
1650    async def fetch_unique_weapon_history(
1651        self,
1652        membership_id: int,
1653        character_id: int,
1654        membership_type: enums.MembershipType | int,
1655    ) -> typedefs.JSONObject:
1656        resp = await self._request(
1657            _GET,
1658            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/UniqueWeapons/",
1659        )
1660        assert isinstance(resp, dict)
1661        return resp
1662
1663    async def fetch_item(
1664        self,
1665        member_id: int,
1666        item_id: int,
1667        membership_type: enums.MembershipType | int,
1668        components: collections.Sequence[enums.ComponentType],
1669    ) -> typedefs.JSONObject:
1670        collector = _collect_components(components)
1671
1672        resp = await self._request(
1673            _GET,
1674            f"Destiny2/{int(membership_type)}/Profile/{member_id}/Item/{item_id}/?components={collector}",
1675        )
1676        assert isinstance(resp, dict)
1677        return resp
1678
1679    async def fetch_clan_weekly_rewards(self, clan_id: int, /) -> typedefs.JSONObject:
1680        resp = await self._request(_GET, f"Destiny2/Clan/{clan_id}/WeeklyRewardState/")
1681        assert isinstance(resp, dict)
1682        return resp
1683
1684    async def fetch_available_locales(self) -> typedefs.JSONObject:
1685        resp = await self._request(_GET, "Destiny2/Manifest/DestinyLocaleDefinition/")
1686        assert isinstance(resp, dict)
1687        return resp
1688
1689    async def fetch_common_settings(self) -> typedefs.JSONObject:
1690        resp = await self._request(_GET, "Settings")
1691        assert isinstance(resp, dict)
1692        return resp
1693
1694    async def fetch_user_systems_overrides(self) -> typedefs.JSONObject:
1695        resp = await self._request(_GET, "UserSystemOverrides")
1696        assert isinstance(resp, dict)
1697        return resp
1698
1699    async def fetch_global_alerts(
1700        self, *, include_streaming: bool = False
1701    ) -> typedefs.JSONArray:
1702        resp = await self._request(
1703            _GET, f"GlobalAlerts/?includestreaming={include_streaming}"
1704        )
1705        assert isinstance(resp, list)
1706        return resp
1707
1708    async def awainitialize_request(
1709        self,
1710        access_token: str,
1711        type: typing.Literal[0, 1],
1712        membership_type: enums.MembershipType | int,
1713        /,
1714        *,
1715        affected_item_id: int | None = None,
1716        character_id: int | None = None,
1717    ) -> typedefs.JSONObject:
1718        body = {"type": type, "membershipType": int(membership_type)}
1719
1720        if affected_item_id is not None:
1721            body["affectedItemId"] = affected_item_id
1722
1723        if character_id is not None:
1724            body["characterId"] = character_id
1725
1726        resp = await self._request(
1727            _POST, "Destiny2/Awa/Initialize", json=body, auth=access_token
1728        )
1729        assert isinstance(resp, dict)
1730        return resp
1731
1732    async def awaget_action_token(
1733        self, access_token: str, correlation_id: str, /
1734    ) -> typedefs.JSONObject:
1735        resp = await self._request(
1736            _POST,
1737            f"Destiny2/Awa/GetActionToken/{correlation_id}",
1738            auth=access_token,
1739        )
1740        assert isinstance(resp, dict)
1741        return resp
1742
1743    async def awa_provide_authorization_result(
1744        self,
1745        access_token: str,
1746        selection: int,
1747        correlation_id: str,
1748        nonce: collections.MutableSequence[str | bytes],
1749    ) -> int:
1750        body = {"selection": selection, "correlationId": correlation_id, "nonce": nonce}
1751
1752        resp = await self._request(
1753            _POST,
1754            "Destiny2/Awa/AwaProvideAuthorizationResult",
1755            json=body,
1756            auth=access_token,
1757        )
1758        assert isinstance(resp, int)
1759        return resp
1760
1761    async def fetch_vendors(
1762        self,
1763        access_token: str,
1764        character_id: int,
1765        membership_id: int,
1766        membership_type: enums.MembershipType | int,
1767        /,
1768        components: collections.Sequence[enums.ComponentType],
1769        filter: int | None = None,
1770    ) -> typedefs.JSONObject:
1771        components_ = _collect_components(components)
1772        route = (
1773            f"Destiny2/{int(membership_type)}/Profile/{membership_id}"
1774            f"/Character/{character_id}/Vendors/?components={components_}"
1775        )
1776
1777        if filter is not None:
1778            route = route + f"&filter={filter}"
1779
1780        resp = await self._request(
1781            _GET,
1782            route,
1783            auth=access_token,
1784        )
1785        assert isinstance(resp, dict)
1786        return resp
1787
1788    async def fetch_vendor(
1789        self,
1790        access_token: str,
1791        character_id: int,
1792        membership_id: int,
1793        membership_type: enums.MembershipType | int,
1794        vendor_hash: int,
1795        /,
1796        components: collections.Sequence[enums.ComponentType],
1797    ) -> typedefs.JSONObject:
1798        components_ = _collect_components(components)
1799        resp = await self._request(
1800            _GET,
1801            (
1802                f"Destiny2/{int(membership_type)}/Profile/{membership_id}"
1803                f"/Character/{character_id}/Vendors/{vendor_hash}/?components={components_}"
1804            ),
1805            auth=access_token,
1806        )
1807        assert isinstance(resp, dict)
1808        return resp
1809
1810    async def fetch_application_api_usage(
1811        self,
1812        access_token: str,
1813        application_id: int,
1814        /,
1815        *,
1816        start: datetime.datetime | None = None,
1817        end: datetime.datetime | None = None,
1818    ) -> typedefs.JSONObject:
1819        end_date, start_date = time.parse_date_range(end, start)
1820        resp = await self._request(
1821            _GET,
1822            f"App/ApiUsage/{application_id}/?end={end_date}&start={start_date}",
1823            auth=access_token,
1824        )
1825        assert isinstance(resp, dict)
1826        return resp
1827
1828    async def fetch_bungie_applications(self) -> typedefs.JSONArray:
1829        resp = await self._request(_GET, "App/FirstParty")
1830        assert isinstance(resp, list)
1831        return resp
1832
1833    async def fetch_content_type(self, type: str, /) -> typedefs.JSONObject:
1834        resp = await self._request(_GET, f"Content/GetContentType/{type}/")
1835        assert isinstance(resp, dict)
1836        return resp
1837
1838    async def fetch_content_by_id(
1839        self, id: int, locale: str, /, *, head: bool = False
1840    ) -> typedefs.JSONObject:
1841        resp = await self._request(
1842            _GET,
1843            f"Content/GetContentById/{id}/{locale}/",
1844            json={"head": head},
1845        )
1846        assert isinstance(resp, dict)
1847        return resp
1848
1849    async def fetch_content_by_tag_and_type(
1850        self, locale: str, tag: str, type: str, *, head: bool = False
1851    ) -> typedefs.JSONObject:
1852        resp = await self._request(
1853            _GET,
1854            f"Content/GetContentByTagAndType/{tag}/{type}/{locale}/",
1855            json={"head": head},
1856        )
1857        assert isinstance(resp, dict)
1858        return resp
1859
1860    async def search_content_with_text(
1861        self,
1862        locale: str,
1863        /,
1864        content_type: str,
1865        search_text: str,
1866        tag: str,
1867        *,
1868        page: int | None = None,
1869        source: str | None = None,
1870    ) -> typedefs.JSONObject:
1871        body: typedefs.JSONObject = {
1872            "locale": locale,
1873            "currentpage": page or 1,
1874            "ctype": content_type,
1875            "searchtxt": search_text,
1876            "searchtext": search_text,
1877            "tag": tag,
1878            "source": source,
1879        }
1880
1881        resp = await self._request(_GET, "Content/Search", params=body)
1882        assert isinstance(resp, dict)
1883        return resp
1884
1885    async def search_content_by_tag_and_type(
1886        self,
1887        locale: str,
1888        tag: str,
1889        type: str,
1890        *,
1891        page: int | None = None,
1892    ) -> typedefs.JSONObject:
1893        body: typedefs.JSONObject = {"currentpage": page or 1}
1894
1895        resp = await self._request(
1896            _GET,
1897            f"Content/SearchContentByTagAndType/{tag}/{type}/{locale}/",
1898            params=body,
1899        )
1900        assert isinstance(resp, dict)
1901        return resp
1902
1903    async def search_help_articles(
1904        self, text: str, size: str, /
1905    ) -> typedefs.JSONObject:
1906        resp = await self._request(_GET, f"Content/SearchHelpArticles/{text}/{size}/")
1907        assert isinstance(resp, dict)
1908        return resp
1909
1910    async def fetch_topics_page(
1911        self,
1912        category_filter: int,
1913        group: int,
1914        date_filter: int,
1915        sort: str | bytes,
1916        *,
1917        page: int | None = None,
1918        locales: collections.Iterable[str] | None = None,
1919        tag_filter: str | None = None,
1920    ) -> typedefs.JSONObject:
1921        params = {
1922            "locales": ",".join(locales) if locales is not None else "en",
1923        }
1924        if tag_filter:
1925            params["tagstring"] = tag_filter
1926
1927        resp = await self._request(
1928            _GET,
1929            f"Forum/GetTopicsPaged/{page or 0}/0/{group}/{sort!s}/{date_filter}/{category_filter}/",
1930            params=params,
1931        )
1932        assert isinstance(resp, dict)
1933        return resp
1934
1935    async def fetch_core_topics_page(
1936        self,
1937        category_filter: int,
1938        date_filter: int,
1939        sort: str | bytes,
1940        *,
1941        page: int | None = None,
1942        locales: collections.Iterable[str] | None = None,
1943    ) -> typedefs.JSONObject:
1944        resp = await self._request(
1945            _GET,
1946            f"Forum/GetCoreTopicsPaged/{page or 0}"
1947            f"/{sort!s}/{date_filter}/{category_filter}/?locales={','.join(locales) if locales else 'en'}",
1948        )
1949        assert isinstance(resp, dict)
1950        return resp
1951
1952    async def fetch_posts_threaded_page(
1953        self,
1954        parent_post: bool,
1955        page: int,
1956        page_size: int,
1957        parent_post_id: int,
1958        reply_size: int,
1959        root_thread_mode: bool,
1960        sort_mode: int,
1961        show_banned: str | None = None,
1962    ) -> typedefs.JSONObject:
1963        resp = await self._request(
1964            _GET,
1965            f"Forum/GetPostsThreadedPaged/{parent_post}/{page}/"
1966            f"{page_size}/{reply_size}/{parent_post_id}/{root_thread_mode}/{sort_mode}/",
1967            json={"showbanned": show_banned},
1968        )
1969        assert isinstance(resp, dict)
1970        return resp
1971
1972    async def fetch_posts_threaded_page_from_child(
1973        self,
1974        child_id: bool,
1975        page: int,
1976        page_size: int,
1977        reply_size: int,
1978        root_thread_mode: bool,
1979        sort_mode: int,
1980        show_banned: str | None = None,
1981    ) -> typedefs.JSONObject:
1982        resp = await self._request(
1983            _GET,
1984            f"Forum/GetPostsThreadedPagedFromChild/{child_id}/"
1985            f"{page}/{page_size}/{reply_size}/{root_thread_mode}/{sort_mode}/",
1986            json={"showbanned": show_banned},
1987        )
1988        assert isinstance(resp, dict)
1989        return resp
1990
1991    async def fetch_post_and_parent(
1992        self, child_id: int, /, *, show_banned: str | None = None
1993    ) -> typedefs.JSONObject:
1994        resp = await self._request(
1995            _GET,
1996            f"Forum/GetPostAndParent/{child_id}/",
1997            json={"showbanned": show_banned},
1998        )
1999        assert isinstance(resp, dict)
2000        return resp
2001
2002    async def fetch_posts_and_parent_awaiting(
2003        self, child_id: int, /, *, show_banned: str | None = None
2004    ) -> typedefs.JSONObject:
2005        resp = await self._request(
2006            _GET,
2007            f"Forum/GetPostAndParentAwaitingApproval/{child_id}/",
2008            json={"showbanned": show_banned},
2009        )
2010        assert isinstance(resp, dict)
2011        return resp
2012
2013    async def fetch_topic_for_content(self, content_id: int, /) -> int:
2014        resp = await self._request(_GET, f"Forum/GetTopicForContent/{content_id}/")
2015        assert isinstance(resp, int)
2016        return resp
2017
2018    async def fetch_forum_tag_suggestions(
2019        self, partial_tag: str, /
2020    ) -> typedefs.JSONObject:
2021        resp = await self._request(
2022            _GET,
2023            "Forum/GetForumTagSuggestions/",
2024            json={"partialtag": partial_tag},
2025        )
2026        assert isinstance(resp, dict)
2027        return resp
2028
2029    async def fetch_poll(self, topic_id: int, /) -> typedefs.JSONObject:
2030        resp = await self._request(_GET, f"Forum/Poll/{topic_id}/")
2031        assert isinstance(resp, dict)
2032        return resp
2033
2034    async def fetch_recruitment_thread_summaries(self) -> typedefs.JSONArray:
2035        resp = await self._request(_POST, "Forum/Recruit/Summaries/")
2036        assert isinstance(resp, list)
2037        return resp
2038
2039    async def fetch_recommended_groups(
2040        self,
2041        access_token: str,
2042        /,
2043        *,
2044        date_range: int = 0,
2045        group_type: enums.GroupType | int = enums.GroupType.CLAN,
2046    ) -> typedefs.JSONArray:
2047        resp = await self._request(
2048            _POST,
2049            f"GroupV2/Recommended/{int(group_type)}/{date_range}/",
2050            auth=access_token,
2051        )
2052        assert isinstance(resp, list)
2053        return resp
2054
2055    async def fetch_available_avatars(self) -> collections.Mapping[str, int]:
2056        resp = await self._request(_GET, "GroupV2/GetAvailableAvatars/")
2057        assert isinstance(resp, dict)
2058        return resp
2059
2060    async def fetch_user_clan_invite_setting(
2061        self,
2062        access_token: str,
2063        /,
2064        membership_type: enums.MembershipType | int,
2065    ) -> bool:
2066        resp = await self._request(
2067            _GET,
2068            f"GroupV2/GetUserClanInviteSetting/{int(membership_type)}/",
2069            auth=access_token,
2070        )
2071        assert isinstance(resp, bool)
2072        return resp
2073
2074    async def fetch_banned_group_members(
2075        self, access_token: str, group_id: int, /, *, page: int = 1
2076    ) -> typedefs.JSONObject:
2077        resp = await self._request(
2078            _GET,
2079            f"GroupV2/{group_id}/Banned/?currentpage={page}",
2080            auth=access_token,
2081        )
2082        assert isinstance(resp, dict)
2083        return resp
2084
2085    async def fetch_pending_group_memberships(
2086        self, access_token: str, group_id: int, /, *, current_page: int = 1
2087    ) -> typedefs.JSONObject:
2088        resp = await self._request(
2089            _GET,
2090            f"GroupV2/{group_id}/Members/Pending/?currentpage={current_page}",
2091            auth=access_token,
2092        )
2093        assert isinstance(resp, dict)
2094        return resp
2095
2096    async def fetch_invited_group_memberships(
2097        self, access_token: str, group_id: int, /, *, current_page: int = 1
2098    ) -> typedefs.JSONObject:
2099        resp = await self._request(
2100            _GET,
2101            f"GroupV2/{group_id}/Members/InvitedIndividuals/?currentpage={current_page}",
2102            auth=access_token,
2103        )
2104        assert isinstance(resp, dict)
2105        return resp
2106
2107    async def invite_member_to_group(
2108        self,
2109        access_token: str,
2110        /,
2111        group_id: int,
2112        membership_id: int,
2113        membership_type: enums.MembershipType | int,
2114        *,
2115        message: str | None = None,
2116    ) -> typedefs.JSONObject:
2117        resp = await self._request(
2118            _POST,
2119            f"GroupV2/{group_id}/Members/IndividualInvite/{int(membership_type)}/{membership_id}/",
2120            auth=access_token,
2121            json={"message": str(message)},
2122        )
2123        assert isinstance(resp, dict)
2124        return resp
2125
2126    async def cancel_group_member_invite(
2127        self,
2128        access_token: str,
2129        /,
2130        group_id: int,
2131        membership_id: int,
2132        membership_type: enums.MembershipType | int,
2133    ) -> typedefs.JSONObject:
2134        resp = await self._request(
2135            _POST,
2136            f"GroupV2/{group_id}/Members/IndividualInviteCancel/{int(membership_type)}/{membership_id}/",
2137            auth=access_token,
2138        )
2139        assert isinstance(resp, dict)
2140        return resp
2141
2142    async def fetch_historical_definition(self) -> typedefs.JSONObject:
2143        resp = await self._request(_GET, "Destiny2/Stats/Definition/")
2144        assert isinstance(resp, dict)
2145        return resp
2146
2147    async def fetch_historical_stats(
2148        self,
2149        character_id: int,
2150        membership_id: int,
2151        membership_type: enums.MembershipType | int,
2152        day_start: datetime.datetime,
2153        day_end: datetime.datetime,
2154        groups: collections.Sequence[enums.StatsGroupType | int],
2155        modes: collections.Sequence[enums.GameMode | int],
2156        *,
2157        period_type: enums.PeriodType = enums.PeriodType.ALL_TIME,
2158    ) -> typedefs.JSONObject:
2159        end, start = time.parse_date_range(day_end, day_start)
2160        resp = await self._request(
2161            _GET,
2162            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/",
2163            json={
2164                "dayend": end,
2165                "daystart": start,
2166                "groups": [str(int(group)) for group in groups],
2167                "modes": [str(int(mode)) for mode in modes],
2168                "periodType": int(period_type),
2169            },
2170        )
2171        assert isinstance(resp, dict)
2172        return resp
2173
2174    async def fetch_historical_stats_for_account(
2175        self,
2176        membership_id: int,
2177        membership_type: enums.MembershipType | int,
2178        groups: collections.Sequence[enums.StatsGroupType | int],
2179    ) -> typedefs.JSONObject:
2180        resp = await self._request(
2181            _GET,
2182            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Stats/",
2183            json={"groups": [str(int(group)) for group in groups]},
2184        )
2185        assert isinstance(resp, dict)
2186        return resp
2187
2188    async def fetch_aggregated_activity_stats(
2189        self,
2190        character_id: int,
2191        membership_id: int,
2192        membership_type: enums.MembershipType | int,
2193        /,
2194    ) -> typedefs.JSONObject:
2195        resp = await self._request(
2196            _GET,
2197            f"Destiny2/{int(membership_type)}/Account/{membership_id}/"
2198            f"Character/{character_id}/Stats/AggregateActivityStats/",
2199        )
2200        assert isinstance(resp, dict)
2201        return resp
2202
2203    async def equip_loadout(
2204        self,
2205        access_token: str,
2206        /,
2207        loadout_index: int,
2208        character_id: int,
2209        membership_type: enums.MembershipType | int,
2210    ) -> None:
2211        response = await self._request(
2212            _POST,
2213            "Destiny2/Actions/Loadouts/EquipLoadout/",
2214            json={
2215                "loadoutIndex": loadout_index,
2216                "characterId": character_id,
2217                "membership_type": int(membership_type),
2218            },
2219            auth=access_token,
2220        )
2221        assert isinstance(response, int)
2222
2223    async def snapshot_loadout(
2224        self,
2225        access_token: str,
2226        /,
2227        loadout_index: int,
2228        character_id: int,
2229        membership_type: enums.MembershipType | int,
2230        *,
2231        color_hash: int | None = None,
2232        icon_hash: int | None = None,
2233        name_hash: int | None = None,
2234    ) -> None:
2235        response = await self._request(
2236            _POST,
2237            "Destiny2/Actions/Loadouts/SnapshotLoadout/",
2238            auth=access_token,
2239            json={
2240                "colorHash": color_hash,
2241                "iconHash": icon_hash,
2242                "nameHash": name_hash,
2243                "loadoutIndex": loadout_index,
2244                "characterId": character_id,
2245                "membershipType": int(membership_type),
2246            },
2247        )
2248        assert isinstance(response, int)
2249
2250    async def update_loadout(
2251        self,
2252        access_token: str,
2253        /,
2254        loadout_index: int,
2255        character_id: int,
2256        membership_type: enums.MembershipType | int,
2257        *,
2258        color_hash: int | None = None,
2259        icon_hash: int | None = None,
2260        name_hash: int | None = None,
2261    ) -> None:
2262        response = await self._request(
2263            _POST,
2264            "Destiny2/Actions/Loadouts/UpdateLoadoutIdentifiers/",
2265            auth=access_token,
2266            json={
2267                "colorHash": color_hash,
2268                "iconHash": icon_hash,
2269                "nameHash": name_hash,
2270                "loadoutIndex": loadout_index,
2271                "characterId": character_id,
2272                "membershipType": int(membership_type),
2273            },
2274        )
2275        assert isinstance(response, int)
2276
2277    async def clear_loadout(
2278        self,
2279        access_token: str,
2280        /,
2281        loadout_index: int,
2282        character_id: int,
2283        membership_type: enums.MembershipType | int,
2284    ) -> None:
2285        response = await self._request(
2286            _POST,
2287            "Destiny2/Actions/Loadouts/ClearLoadout/",
2288            json={
2289                "loadoutIndex": loadout_index,
2290                "characterId": character_id,
2291                "membership_type": int(membership_type),
2292            },
2293            auth=access_token,
2294        )
2295        assert isinstance(response, int)

A RESTful client implementation for Bungie's API.

This client is designed to only make HTTP requests and return JSON objects to provide RESTful functionality.

This client is the core for aiobungie.Client which deserialize those returned JSON objects using the factory into Pythonic data classes objects which provide Python functionality.

Example
import aiobungie

client = aiobungie.RESTClient("TOKEN")
async with client:
    response = await client.fetch_clan_members(4389205)
    for member in response['results']:
        for key, value in member['destinyUserInfo'].items():
            print(key, value)
Parameters
  • token (str): A valid application token from Bungie's developer portal.
Other Parameters
  • max_retries (int): The max retries number to retry if the request hit a 5xx status code.
  • client_secret (str | None): An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client.
  • client_id (int | None): An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client.
  • debug (bool | str): Whether to enable logging responses or not.
Logging Levels
  • False: This will disable logging.
  • True: This will set the level to DEBUG and enable logging minimal information.
  • "TRACE" | TRACE: This will log the response headers along with the minimal information.
RESTClient( token: str, /, *, client_secret: str | None = None, client_id: int | None = None, client_session: aiohttp.client.ClientSession | None = None, dumps: collections.abc.Callable[[collections.abc.Mapping[str, typing.Any] | collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]]], bytes] = <function dumps>, loads: collections.abc.Callable[[str | bytes], collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]] | collections.abc.Mapping[str, typing.Any]] = <function loads>, max_retries: int = 4, debug: Union[Literal['TRACE'], bool, int] = False)
369    def __init__(
370        self,
371        token: str,
372        /,
373        *,
374        client_secret: str | None = None,
375        client_id: int | None = None,
376        client_session: aiohttp.ClientSession | None = None,
377        dumps: typedefs.Dumps = helpers.dumps,
378        loads: typedefs.Loads = helpers.loads,
379        max_retries: int = 4,
380        debug: typing.Literal["TRACE"] | bool | int = False,
381    ) -> None:
382        self._session = client_session
383        self._lock: asyncio.Lock | None = None
384        self._client_secret = client_secret
385        self._client_id = client_id
386        self._token: str = token
387        self._max_retries = max_retries
388        self._dumps = dumps
389        self._loads = loads
390        self._print = False
391        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
392        self.with_debug(debug)
client_id: int | None
394    @property
395    def client_id(self) -> int | None:
396        return self._client_id

Return the client id of this REST client if provided, Otherwise None.

metadata: collections.abc.MutableMapping[typing.Any, typing.Any]
398    @property
399    def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]:
400        return self._metadata

A mutable mapping storage for the user's needs.

This mapping is useful for storing any kind of data that the user may need.

Example
import aiobungie

client = aiobungie.RESTClient(…)

async with client:
    # Fetch auth tokens and store them
    client.metadata["tokens"] = await client.fetch_access_token("code")

# Some other time.
async with client:
    # Retrieve the tokens
    tokens: aiobungie.OAuth2Response = client.metadata["tokens"]

    # Use them to fetch your user.
    user = await client.fetch_current_user_memberships(tokens.access_token)
is_alive: bool
402    @property
403    def is_alive(self) -> bool:
404        return self._session is not None

Returns True if the REST client is alive and False otherwise.

@typing.final
async def close(self) -> None:
406    @typing.final
407    async def close(self) -> None:
408        if self._session is None:
409            raise RuntimeError("REST client is not running.")
410
411        await self._session.close()
412        self._session = None

Close this REST client session if it was acquired.

This method is automatically called when using async with contextmanager.

Raises
  • RuntimeError: If the client is already closed.
@typing.final
def open(self) -> None:
414    @typing.final
415    def open(self) -> None:
416        """Open a new client session. This is called internally with contextmanager usage."""
417        if self._session:
418            raise RuntimeError("Cannot open REST client when it's already open.")
419
420        self._session = aiohttp.ClientSession(
421            connector=aiohttp.TCPConnector(),
422            connector_owner=True,
423            raise_for_status=False,
424            timeout=aiohttp.ClientTimeout(total=30.0),
425        )

Open a new client session. This is called internally with contextmanager usage.

@typing.final
async def static_request( self, method: Literal['GET', 'DELETE', 'POST', 'PUT', 'PATCH'], path: str, *, auth: str | None = None, json: collections.abc.Mapping[str, typing.Any] | None = None) -> collections.abc.Mapping[str, typing.Any] | collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]] | bytes | str | int | bool | None:
427    @typing.final
428    async def static_request(
429        self,
430        method: _HTTP_METHOD,
431        path: str,
432        *,
433        auth: str | None = None,
434        json: collections.Mapping[str, typing.Any] | None = None,
435    ) -> typedefs.JSONIsh:
436        return await self._request(method, path, auth=auth, json=json)

Perform an HTTP request given a valid Bungie endpoint.

Parameters
  • method (str): The request method, This may be GET, POST, PUT, etc.
  • path (str): The Bungie endpoint or path. A path must look something like this Destiny2/3/Profile/46111239123/...
Other Parameters
  • auth (str | None): An optional bearer token for methods that requires OAuth2 Authorization header.
  • json (MutableMapping[str, typing.Any] | None): An optional JSON mapping to include in the request.
Returns
  • aiobungie.typedefs.JSONIsh: The response payload.
@typing.final
def build_oauth2_url(self, client_id: int | None = None) -> aiobungie.builders.OAuthURL | None:
438    @typing.final
439    def build_oauth2_url(
440        self, client_id: int | None = None
441    ) -> builders.OAuthURL | None:
442        client_id = client_id or self._client_id
443        if client_id is None:
444            return None
445
446        return builders.OAuthURL(client_id=client_id)

Builds an OAuth2 URL using the provided user REST/Base client secret/id.

You can't get the complete string URL by using .compile() method.

Parameters
  • client_id (int | None): An optional client id to provide, If left None it will roll back to the id passed to the RESTClient, If both is None this method will return None.
Returns
@typing.final
def with_debug( self, level: Union[Literal['TRACE'], bool, int] = True, file: str | pathlib.Path | None = None) -> None:
448    @typing.final
449    def with_debug(
450        self,
451        level: typing.Literal["TRACE"] | bool | int = True,
452        file: str | pathlib.Path | None = None,
453    ) -> None:
454        """Enable debugging for this client with a level. Defaults to `True`.
455
456        Parameters
457        ----------
458        level: `NotRequired[int | bool | typing.Literal["TRACE"] | None]`
459            The level of the logger. This field is not required.
460        file: `pathlib.Path | str | None`
461            An optional file to write the logs into.
462
463        Logging Levels
464        --------------
465        * `False`: This will disable logging.
466        * `True`: This will set the level to `DEBUG` and enable logging minimal information.
467        * `"TRACE" | aiobungie.TRACE`: This will log the response headers along with the minimal information.
468        """
469        logging.logThreads = False
470        logging.logMultiprocessing = False
471        logging.logProcesses = False
472        logging.captureWarnings(True)
473
474        format = "%(levelname)s " "%(asctime)23.23s " "%(name)s: " "%(message)s"
475
476        file_handler = (logging.FileHandler(file),) if file else None
477        if level == "TRACE" or level == TRACE:
478            logging.basicConfig(
479                level=TRACE, format=format, stream=sys.stdout, handlers=file_handler
480            )
481
482        elif level is True:
483            logging.basicConfig(
484                level=logging.DEBUG,
485                format=format,
486                stream=sys.stdout,
487                handlers=file_handler,
488            )

Enable debugging for this client with a level. Defaults to True.

Parameters
  • level (NotRequired[int | bool | typing.Literal["TRACE"] | None]): The level of the logger. This field is not required.
  • file (pathlib.Path | str | None): An optional file to write the logs into.
Logging Levels
  • False: This will disable logging.
  • True: This will set the level to DEBUG and enable logging minimal information.
  • "TRACE" | TRACE: This will log the response headers along with the minimal information.
async def fetch_oauth2_tokens(self, code: str, /) -> aiobungie.builders.OAuth2Response:
714    async def fetch_oauth2_tokens(self, code: str, /) -> builders.OAuth2Response:
715        data = {
716            "grant_type": "authorization_code",
717            "code": code,
718            "client_id": self._client_id,
719            "client_secret": self._client_secret,
720        }
721
722        response = await self._request(_POST, "", data=data, oauth2=True)
723        assert isinstance(response, dict)
724        return builders.OAuth2Response.build_response(response)

Makes a POST request and fetch the OAuth2 access_token and refresh token.

Parameters
  • code (str): The Authorization code received from the authorization endpoint found in the URL parameters.
Returns
Raises
async def refresh_access_token(self, refresh_token: str, /) -> aiobungie.builders.OAuth2Response:
726    async def refresh_access_token(
727        self, refresh_token: str, /
728    ) -> builders.OAuth2Response:
729        data = {
730            "grant_type": "refresh_token",
731            "refresh_token": refresh_token,
732            "client_id": self._client_id,
733            "client_secret": self._client_secret,
734        }
735
736        response = await self._request(_POST, "", data=data, oauth2=True)
737        assert isinstance(response, dict)
738        return builders.OAuth2Response.build_response(response)

Refresh OAuth2 access token given its refresh token.

Parameters
  • refresh_token (str): The refresh token.
Returns
async def fetch_bungie_user(self, id: int) -> collections.abc.Mapping[str, typing.Any]:
740    async def fetch_bungie_user(self, id: int) -> typedefs.JSONObject:
741        resp = await self._request(_GET, f"User/GetBungieNetUserById/{id}/")
742        assert isinstance(resp, dict)
743        return resp

Fetch a Bungie user by their id.

Parameters
  • id (int): The user id.
Returns
Raises
async def fetch_user_themes( self) -> collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]]:
745    async def fetch_user_themes(self) -> typedefs.JSONArray:
746        resp = await self._request(_GET, "User/GetAvailableThemes/")
747        assert isinstance(resp, list)
748        return resp

Fetch all available user themes.

Returns
async def fetch_membership_from_id( self, id: int, type: MembershipType | int = <MembershipType.NONE: 0>, /) -> collections.abc.Mapping[str, typing.Any]:
750    async def fetch_membership_from_id(
751        self,
752        id: int,
753        type: enums.MembershipType | int = enums.MembershipType.NONE,
754        /,
755    ) -> typedefs.JSONObject:
756        resp = await self._request(_GET, f"User/GetMembershipsById/{id}/{int(type)}")
757        assert isinstance(resp, dict)
758        return resp

Fetch Bungie user's memberships from their id.

Parameters
  • id (int): The user's id.
  • type (aiobungie.aiobungie.MembershipType | int): The user's membership type.
Returns
Raises
async def fetch_membership( self, name: str, code: int, type: MembershipType | int = <MembershipType.ALL: -1>, /) -> collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]]:
760    async def fetch_membership(
761        self,
762        name: str,
763        code: int,
764        type: enums.MembershipType | int = enums.MembershipType.ALL,
765        /,
766    ) -> typedefs.JSONArray:
767        resp = await self._request(
768            _POST,
769            f"Destiny2/SearchDestinyPlayerByBungieName/{int(type)}",
770            json={"displayName": name, "displayNameCode": code},
771        )
772        assert isinstance(resp, list)
773        return resp

Fetch a Destiny 2 Player.

Parameters
  • name (str): The unique Bungie player name.
  • code (int): The unique Bungie display name code.
  • type (aiobungie.aiobungie.MembershipType | int): The player's membership type, e,g. XBOX, STEAM, PSN
Returns
Raises
async def search_users(self, name: str, /) -> collections.abc.Mapping[str, typing.Any]:
775    async def search_users(self, name: str, /) -> typedefs.JSONObject:
776        resp = await self._request(
777            _POST,
778            "User/Search/GlobalName/0",
779            json={"displayNamePrefix": name},
780        )
781        assert isinstance(resp, dict)
782        return resp

Search for users by their global name and return all users who share this name.

Parameters
  • name (str): The user name.
Returns
Raises
async def fetch_clan_from_id( self, id: int, /, access_token: str | None = None) -> collections.abc.Mapping[str, typing.Any]:
784    async def fetch_clan_from_id(
785        self, id: int, /, access_token: str | None = None
786    ) -> typedefs.JSONObject:
787        resp = await self._request(_GET, f"GroupV2/{id}", auth=access_token)
788        assert isinstance(resp, dict)
789        return resp

Fetch a Bungie Clan by its id.

Parameters
  • id (int): The clan id.
Other Parameters
  • access_token (str | None): An optional access token to make the request with.

    If the token was bound to a member of the clan, This field aiobungie.crates.Clan.current_user_membership will be available and will return the membership of the user who made this request.

Returns
Raises
async def fetch_clan( self, name: str, /, access_token: str | None = None, *, type: GroupType | int = <GroupType.CLAN: 1>) -> collections.abc.Mapping[str, typing.Any]:
791    async def fetch_clan(
792        self,
793        name: str,
794        /,
795        access_token: str | None = None,
796        *,
797        type: enums.GroupType | int = enums.GroupType.CLAN,
798    ) -> typedefs.JSONObject:
799        resp = await self._request(
800            _GET, f"GroupV2/Name/{name}/{int(type)}", auth=access_token
801        )
802        assert isinstance(resp, dict)
803        return resp

Fetch a Clan by its name. This method will return the first clan found with given name name.

Parameters
  • name (str): The clan name.
Other Parameters
  • access_token (str | None): An optional access token to make the request with.

    If the token was bound to a member of the clan, This field aiobungie.crates.Clan.current_user_membership will be available and will return the membership of the user who made this request.

  • type (aiobungie.aiobungie.GroupType | int): The group type, Default is one.
Returns
Raises
async def fetch_clan_admins(self, clan_id: int, /) -> collections.abc.Mapping[str, typing.Any]:
805    async def fetch_clan_admins(self, clan_id: int, /) -> typedefs.JSONObject:
806        resp = await self._request(_GET, f"GroupV2/{clan_id}/AdminsAndFounder/")
807        assert isinstance(resp, dict)
808        return resp

Fetch the admins and founder members of the clan.

Parameters
  • clan_id (int): The clan id.
Returns
Raises
async def fetch_clan_conversations( self, clan_id: int, /) -> collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]]:
810    async def fetch_clan_conversations(self, clan_id: int, /) -> typedefs.JSONArray:
811        resp = await self._request(_GET, f"GroupV2/{clan_id}/OptionalConversations/")
812        assert isinstance(resp, list)
813        return resp

Fetch a clan's conversations.

Parameters
  • clan_id (int): The clan's id.
Returns
async def fetch_application(self, appid: int, /) -> collections.abc.Mapping[str, typing.Any]:
815    async def fetch_application(self, appid: int, /) -> typedefs.JSONObject:
816        resp = await self._request(_GET, f"App/Application/{appid}")
817        assert isinstance(resp, dict)
818        return resp

Fetch a Bungie Application.

Parameters
  • appid (int): The application id.
Returns
async def fetch_character( self, member_id: int, membership_type: MembershipType | int, character_id: int, components: collections.abc.Sequence[ComponentType], auth: str | None = None) -> collections.abc.Mapping[str, typing.Any]:
820    async def fetch_character(
821        self,
822        member_id: int,
823        membership_type: enums.MembershipType | int,
824        character_id: int,
825        components: collections.Sequence[enums.ComponentType],
826        auth: str | None = None,
827    ) -> typedefs.JSONObject:
828        collector = _collect_components(components)
829        response = await self._request(
830            _GET,
831            f"Destiny2/{int(membership_type)}/Profile/{member_id}/"
832            f"Character/{character_id}/?components={collector}",
833            auth=auth,
834        )
835        assert isinstance(response, dict)
836        return response

Fetch a Destiny 2 player's characters.

Parameters
  • member_id (int): A valid bungie member id.
  • membership_type (aiobungie.aiobungie.internal.enums.MembershipType | int): The member's membership type.
  • character_id (int): The character id to return.
  • components (collections.Sequence[aiobungie.ComponentType]): A list of character components to collect and return.
Other Parameters
  • auth (str | None): A bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
Raises
async def fetch_activities( self, member_id: int, character_id: int, mode: GameMode | int, membership_type: MembershipType | int = <MembershipType.ALL: -1>, *, page: int = 0, limit: int = 1) -> collections.abc.Mapping[str, typing.Any]:
838    async def fetch_activities(
839        self,
840        member_id: int,
841        character_id: int,
842        mode: enums.GameMode | int,
843        membership_type: enums.MembershipType | int = enums.MembershipType.ALL,
844        *,
845        page: int = 0,
846        limit: int = 1,
847    ) -> typedefs.JSONObject:
848        resp = await self._request(
849            _GET,
850            f"Destiny2/{int(membership_type)}/Account/"
851            f"{member_id}/Character/{character_id}/Stats/Activities"
852            f"/?mode={int(mode)}&count={limit}&page={page}",
853        )
854        assert isinstance(resp, dict)
855        return resp

Fetch a Destiny 2 activity for the specified user id and character.

Parameters
  • member_id (int): The user id that starts with 4611.
  • character_id (int): The id of the character to retrieve.
  • mode (aiobungie.aiobungie.GameMode | int): This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
Other Parameters
  • membership_type (aiobungie.aiobungie.internal.enums.MembershipType | int): The Member ship type, if nothing was passed than it will return all.
  • page (int): The page number. Default to 1
  • limit (int): Limit the returned result. Default to 1
Returns
Raises
async def fetch_vendor_sales(self) -> collections.abc.Mapping[str, typing.Any]:
857    async def fetch_vendor_sales(self) -> typedefs.JSONObject:
858        resp = await self._request(
859            _GET,
860            f"Destiny2/Vendors/?components={int(enums.ComponentType.VENDOR_SALES)}",
861        )
862        assert isinstance(resp, dict)
863        return resp
async def fetch_profile( self, membership_id: int, type: MembershipType | int, components: collections.abc.Sequence[ComponentType], auth: str | None = None) -> collections.abc.Mapping[str, typing.Any]:
865    async def fetch_profile(
866        self,
867        membership_id: int,
868        type: enums.MembershipType | int,
869        components: collections.Sequence[enums.ComponentType],
870        auth: str | None = None,
871    ) -> typedefs.JSONObject:
872        collector = _collect_components(components)
873        response = await self._request(
874            _GET,
875            f"Destiny2/{int(type)}/Profile/{membership_id}/?components={collector}",
876            auth=auth,
877        )
878        assert isinstance(response, dict)
879        return response

Fetch a bungie profile.

Parameters
  • membership_id (int): The member's id.
  • type (aiobungie.aiobungie.MembershipType | int): A valid membership type.
  • components (collections.Sequence[aiobungie.ComponentType]): A sequence of profile components to collect and return.
Other Parameters
  • auth (str | None): A bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
Raises
async def fetch_entity(self, type: str, hash: int) -> collections.abc.Mapping[str, typing.Any]:
881    async def fetch_entity(self, type: str, hash: int) -> typedefs.JSONObject:
882        response = await self._request(_GET, route=f"Destiny2/Manifest/{type}/{hash}")
883        assert isinstance(response, dict)
884        return response

Fetch a Destiny definition item given its type and hash.

Parameters
  • type (str): Entity's type definition.
  • hash (int): Entity's hash.
Returns
async def fetch_inventory_item(self, hash: int, /) -> collections.abc.Mapping[str, typing.Any]:
886    async def fetch_inventory_item(self, hash: int, /) -> typedefs.JSONObject:
887        resp = await self.fetch_entity("DestinyInventoryItemDefinition", hash)
888        assert isinstance(resp, dict)
889        return resp

Fetch a Destiny inventory item entity given a its hash.

Parameters
  • hash (int): Entity's hash.
Returns
async def fetch_objective_entity(self, hash: int, /) -> collections.abc.Mapping[str, typing.Any]:
891    async def fetch_objective_entity(self, hash: int, /) -> typedefs.JSONObject:
892        resp = await self.fetch_entity("DestinyObjectiveDefinition", hash)
893        assert isinstance(resp, dict)
894        return resp

Fetch a Destiny objective entity given a its hash.

Parameters
  • hash (int): objective's hash.
Returns
async def fetch_groups_for_member( self, member_id: int, member_type: MembershipType | int, /, *, filter: int = 0, group_type: GroupType | int = <GroupType.CLAN: 1>) -> collections.abc.Mapping[str, typing.Any]:
896    async def fetch_groups_for_member(
897        self,
898        member_id: int,
899        member_type: enums.MembershipType | int,
900        /,
901        *,
902        filter: int = 0,
903        group_type: enums.GroupType | int = enums.GroupType.CLAN,
904    ) -> typedefs.JSONObject:
905        resp = await self._request(
906            _GET,
907            f"GroupV2/User/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
908        )
909        assert isinstance(resp, dict)
910        return resp

Fetch the information about the groups for a member.

Parameters
  • member_id (int): The member's id
  • member_type (aiobungie.aiobungie.MembershipType | int): The member's membership type.
Other Parameters
  • filter (int): Filter apply to list of joined groups. This Default to 0
  • group_type (aiobungie.aiobungie.GroupType | int): The group's type. This is always set to aiobungie.GroupType.CLAN and should not be changed.
Returns
async def fetch_potential_groups_for_member( self, member_id: int, member_type: MembershipType | int, /, *, filter: int = 0, group_type: GroupType | int = <GroupType.CLAN: 1>) -> collections.abc.Mapping[str, typing.Any]:
912    async def fetch_potential_groups_for_member(
913        self,
914        member_id: int,
915        member_type: enums.MembershipType | int,
916        /,
917        *,
918        filter: int = 0,
919        group_type: enums.GroupType | int = enums.GroupType.CLAN,
920    ) -> typedefs.JSONObject:
921        resp = await self._request(
922            _GET,
923            f"GroupV2/User/Potential/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
924        )
925        assert isinstance(resp, dict)
926        return resp

Get information about the groups that a given member has applied to or been invited to.

Parameters
  • member_id (int): The member's id
  • member_type (aiobungie.aiobungie.MembershipType | int): The member's membership type.
Other Parameters
  • filter (int): Filter apply to list of joined groups. This Default to 0
  • group_type (aiobungie.aiobungie.GroupType | int): The group's type. This is always set to aiobungie.GroupType.CLAN and should not be changed.
Returns
async def fetch_clan_members( self, clan_id: int, /, *, name: str | None = None, type: MembershipType | int = <MembershipType.NONE: 0>) -> collections.abc.Mapping[str, typing.Any]:
928    async def fetch_clan_members(
929        self,
930        clan_id: int,
931        /,
932        *,
933        name: str | None = None,
934        type: enums.MembershipType | int = enums.MembershipType.NONE,
935    ) -> typedefs.JSONObject:
936        resp = await self._request(
937            _GET,
938            f"/GroupV2/{clan_id}/Members/?memberType={int(type)}&nameSearch={name if name else ''}&currentpage=1",
939        )
940        assert isinstance(resp, dict)
941        return resp

Fetch all Bungie Clan members.

Parameters
  • clan_id (int): The clans id
Other Parameters
  • name (str | None): If provided, Only players matching this name will be returned.
  • type (aiobungie.aiobungie.MembershipType | int): An optional clan member's membership type. Default is set to aiobungie.MembershipType.NONE Which returns the first matched clan member by their name.
Returns
Raises
async def fetch_hardlinked_credentials( self, credential: int, type: CredentialType | int = <CredentialType.STEAMID: 12>, /) -> collections.abc.Mapping[str, typing.Any]:
943    async def fetch_hardlinked_credentials(
944        self,
945        credential: int,
946        type: enums.CredentialType | int = enums.CredentialType.STEAMID,
947        /,
948    ) -> typedefs.JSONObject:
949        resp = await self._request(
950            _GET,
951            f"User/GetMembershipFromHardLinkedCredential/{int(type)}/{credential}/",
952        )
953        assert isinstance(resp, dict)
954        return resp

Gets any hard linked membership given a credential.

Only works for credentials that are public just aiobungie.CredentialType.STEAMID right now. Cross Save aware.

Parameters
  • credential (int): A valid SteamID64
  • type (aiobungie.aiobungie.CredentialType | int): The credential type. This must not be changed Since its only credential that works "currently"
Returns
async def fetch_user_credentials( self, access_token: str, membership_id: int, /) -> collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]]:
956    async def fetch_user_credentials(
957        self, access_token: str, membership_id: int, /
958    ) -> typedefs.JSONArray:
959        resp = await self._request(
960            _GET,
961            f"User/GetCredentialTypesForTargetAccount/{membership_id}",
962            auth=access_token,
963        )
964        assert isinstance(resp, list)
965        return resp

Fetch an array of credential types attached to the requested account.

This method require OAuth2 Bearer access token.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • membership_id (int): The id of the membership to return.
Returns
Raises
async def insert_socket_plug( self, action_token: str, /, instance_id: int, plug: aiobungie.builders.PlugSocketBuilder | collections.abc.Mapping[str, int], character_id: int, membership_type: MembershipType | int) -> collections.abc.Mapping[str, typing.Any]:
967    async def insert_socket_plug(
968        self,
969        action_token: str,
970        /,
971        instance_id: int,
972        plug: builders.PlugSocketBuilder | collections.Mapping[str, int],
973        character_id: int,
974        membership_type: enums.MembershipType | int,
975    ) -> typedefs.JSONObject:
976        if isinstance(plug, builders.PlugSocketBuilder):
977            plug = plug.collect()
978
979        body = {
980            "actionToken": action_token,
981            "itemInstanceId": instance_id,
982            "plug": plug,
983            "characterId": character_id,
984            "membershipType": int(membership_type),
985        }
986        resp = await self._request(
987            _POST, "Destiny2/Actions/Items/InsertSocketPlug", json=body
988        )
989        assert isinstance(resp, dict)
990        return resp

Insert a plug into a socketed item.

OAuth2: AdvancedWriteActions scope is required

Parameters
  • action_token (str): Action token provided by the AwaGetActionToken API call.
  • instance_id (int): The item instance id that's plug inserted.
  • plug (aiobungie.builders.PlugSocketBuilder | Mapping[str, int]): Either a PlugSocketBuilder object or a raw dict contains key, value for the plug entries.
Example
plug = (
    aiobungie.PlugSocketBuilder()
    .set_socket_array(0)
    .set_socket_index(0)
    .set_plug_item(3023847)
    .collect()
)
await insert_socket_plug_free(..., plug=plug)

character_id : int The character's id. membership_type : aiobungie.aiobungie.MembershipType | int The membership type.

Returns
Raises
async def insert_socket_plug_free( self, access_token: str, /, instance_id: int, plug: aiobungie.builders.PlugSocketBuilder | collections.abc.Mapping[str, int], character_id: int, membership_type: MembershipType | int) -> collections.abc.Mapping[str, typing.Any]:
 992    async def insert_socket_plug_free(
 993        self,
 994        access_token: str,
 995        /,
 996        instance_id: int,
 997        plug: builders.PlugSocketBuilder | collections.Mapping[str, int],
 998        character_id: int,
 999        membership_type: enums.MembershipType | int,
1000    ) -> typedefs.JSONObject:
1001        if isinstance(plug, builders.PlugSocketBuilder):
1002            plug = plug.collect()
1003
1004        body = {
1005            "itemInstanceId": instance_id,
1006            "plug": plug,
1007            "characterId": character_id,
1008            "membershipType": int(membership_type),
1009        }
1010        resp = await self._request(
1011            _POST,
1012            "Destiny2/Actions/Items/InsertSocketPlugFree",
1013            json=body,
1014            auth=access_token,
1015        )
1016        assert isinstance(resp, dict)
1017        return resp

Insert a plug into a socketed item. This doesn't require an Action token.

OAuth2: MoveEquipDestinyItems scope is required

Parameters
  • instance_id (int): The item instance id that's plug inserted.
  • plug (aiobungie.builders.PlugSocketBuilder | Mapping[str, int]): Either a PlugSocketBuilder object or a raw dict contains key, value for the plug entries.
Example
plug = (
    aiobungie.PlugSocketBuilder()
    .set_socket_array(0)
    .set_socket_index(0)
    .set_plug_item(3023847)
    .collect()
)
await insert_socket_plug_free(..., plug=plug)

character_id : int The character's id. membership_type : aiobungie.aiobungie.MembershipType | int The membership type.

Returns
Raises
@helpers.unstable
async def set_item_lock_state( self, access_token: str, state: bool, /, item_id: int, character_id: int, membership_type: MembershipType | int) -> int:
1019    @helpers.unstable
1020    async def set_item_lock_state(
1021        self,
1022        access_token: str,
1023        state: bool,
1024        /,
1025        item_id: int,
1026        character_id: int,
1027        membership_type: enums.MembershipType | int,
1028    ) -> int:
1029        self._print = True
1030        body = {
1031            "state": state,
1032            "itemId": item_id,
1033            "characterId": character_id,
1034            "membershipType": int(membership_type),
1035        }
1036        response = await self._request(
1037            _POST,
1038            "Destiny2/Actions/Items/SetLockState",
1039            json=body,
1040            auth=access_token,
1041        )
1042        assert isinstance(response, int)
1043        return response

Set the Lock State for an instanced item.

OAuth2: MoveEquipDestinyItems scope is required

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • state (bool): If True, The item will be locked, If False, The item will be unlocked.
  • item_id (int): The item id.
  • character_id (int): The character id.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The membership type for the associated account.
Returns
  • int: An integer represents whether the request was successful or failed.
Raises
async def set_quest_track_state( self, access_token: str, state: bool, /, item_id: int, character_id: int, membership_type: MembershipType | int) -> int:
1045    async def set_quest_track_state(
1046        self,
1047        access_token: str,
1048        state: bool,
1049        /,
1050        item_id: int,
1051        character_id: int,
1052        membership_type: enums.MembershipType | int,
1053    ) -> int:
1054        body = {
1055            "state": state,
1056            "itemId": item_id,
1057            "characterId": character_id,
1058            "membership_type": int(membership_type),
1059        }
1060        response = await self._request(
1061            _POST,
1062            "Destiny2/Actions/Items/SetTrackedState",
1063            json=body,
1064            auth=access_token,
1065        )
1066        assert isinstance(response, int)
1067        return response

Set the Tracking State for an instanced Quest or Bounty.

OAuth2: MoveEquipDestinyItems scope is required

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • state (bool): If True, The item will be locked, If False, The item will be unlocked.
  • item_id (int): The item id.
  • character_id (int): The character id.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The membership type for the associated account.
Returns
  • int: An integer represents whether the request was successful or failed.
Raises
async def fetch_manifest_path(self) -> collections.abc.Mapping[str, typing.Any]:
1069    async def fetch_manifest_path(self) -> typedefs.JSONObject:
1070        path = await self._request(_GET, "Destiny2/Manifest")
1071        assert isinstance(path, dict)
1072        return path

Fetch the manifest JSON paths.

Returns
  • typedefs.JSONObject: The manifest JSON paths.
async def read_manifest_bytes(self, language: str = 'en', /) -> bytes:
1074    async def read_manifest_bytes(self, language: str = "en", /) -> bytes:
1075        _ensure_manifest_language(language)
1076
1077        content = await self.fetch_manifest_path()
1078        resp = await self._request(
1079            _GET,
1080            content["mobileWorldContentPaths"][language],
1081            unwrap_bytes=True,
1082            base=True,
1083        )
1084        assert isinstance(resp, bytes)
1085        return resp

Read raw manifest SQLite database bytes response.

This method can be used to write the bytes to zipped file and then extract it to get the manifest content.

Parameters
  • language (str): The manifest database language bytes to get.
Returns
  • bytes: The bytes to read and write the manifest database.
async def download_sqlite_manifest( self, language: str = 'en', name: str = 'manifest', path: pathlib.Path | str = '.', *, force: bool = False) -> pathlib.Path:
1087    async def download_sqlite_manifest(
1088        self,
1089        language: str = "en",
1090        name: str = "manifest",
1091        path: pathlib.Path | str = ".",
1092        *,
1093        force: bool = False,
1094    ) -> pathlib.Path:
1095        complete_path = _get_path(name, path, sql=True)
1096
1097        if complete_path.exists() and force:
1098            if force:
1099                _LOGGER.info(
1100                    f"Found manifest in {complete_path!s}. Forcing to Re-Download."
1101                )
1102                complete_path.unlink(missing_ok=True)
1103
1104                return await self.download_sqlite_manifest(
1105                    language, name, path, force=force
1106                )
1107
1108            else:
1109                raise FileExistsError(
1110                    "Manifest file already exists, "
1111                    "To force download, set the `force` parameter to `True`."
1112                )
1113
1114        _LOGGER.info(f"Downloading manifest. Location: {complete_path!s}")
1115        data_bytes = await self.read_manifest_bytes(language)
1116        await asyncio.get_running_loop().run_in_executor(
1117            None, _write_sqlite_bytes, data_bytes, path, name
1118        )
1119        _LOGGER.info("Finished downloading manifest.")
1120        return _get_path(name, path, sql=True)

Downloads the SQLite version of Destiny2's Manifest.

Example
manifest = await rest.download_sqlite_manifest()
with sqlite3.connect(manifest) as conn:
    ...
Parameters
  • language (str): The manifest language to download, Default is English.
  • path (str | pathlib.Path): The path to download this manifest. Example "/tmp/databases/", Default is the current directory.
  • name (str): The manifest database file name. Default is manifest
  • force (bool): Whether to force the download. Default is False. However if set to true the old file will get unlinked and a new one will begin to download.
Returns
  • pathlib.Path: A pathlib object of the .sqlite file.
Raises
  • FileNotFoundError: If the manifest file exists and force is False.
  • ValueError: If the provided language was not recognized.
async def download_json_manifest( self, file_name: str = 'manifest', path: str | pathlib.Path = '.', language: str = 'en') -> pathlib.Path:
1122    async def download_json_manifest(
1123        self,
1124        file_name: str = "manifest",
1125        path: str | pathlib.Path = ".",
1126        language: str = "en",
1127    ) -> pathlib.Path:
1128        _ensure_manifest_language(language)
1129        #
1130        full_path = _get_path(file_name, path)
1131        _LOGGER.info(f"Downloading manifest JSON to {full_path!r}...")
1132
1133        content = await self.fetch_manifest_path()
1134        json_bytes = await self._request(
1135            _GET,
1136            content["jsonWorldContentPaths"][language],
1137            unwrap_bytes=True,
1138            base=True,
1139        )
1140
1141        assert isinstance(json_bytes, bytes)
1142        await asyncio.get_running_loop().run_in_executor(
1143            None, _write_json_bytes, json_bytes, file_name, path
1144        )
1145        _LOGGER.info("Finished downloading manifest JSON.")
1146        return full_path

Download the Bungie manifest json file.

Example
manifest = await rest.download_json_manifest()
with open(manifest, "r") as f:
    to_dict = json.loads(f.read())
    item_definitions = to_dict['DestinyInventoryItemDefinition']
Parameters
  • file_name (str): The file name to save the manifest json file. Default is manifest.
  • path (str | pathlib.Path): The path to save the manifest json file. Default is the current directory. Example "D:/"
  • language (str): The manifest database language bytes to get. Default is English.
Returns
  • pathlib.Path: The path of this JSON manifest.
async def fetch_manifest_version(self) -> str:
1148    async def fetch_manifest_version(self) -> str:
1149        # This is guaranteed str.
1150        return (await self.fetch_manifest_path())["version"]

Fetch the manifest version.

Returns
  • str: The manifest version.
async def fetch_linked_profiles( self, member_id: int, member_type: MembershipType | int, /, *, all: bool = False) -> collections.abc.Mapping[str, typing.Any]:
1152    async def fetch_linked_profiles(
1153        self,
1154        member_id: int,
1155        member_type: enums.MembershipType | int,
1156        /,
1157        *,
1158        all: bool = False,
1159    ) -> typedefs.JSONObject:
1160        resp = await self._request(
1161            _GET,
1162            f"Destiny2/{int(member_type)}/Profile/{member_id}/LinkedProfiles/?getAllMemberships={all}",
1163        )
1164        assert isinstance(resp, dict)
1165        return resp

Returns a summary information about all profiles linked to the requested member.

The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.

It will only return linked accounts whose linkages you are allowed to view.

Parameters
  • member_id (int): The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID.
  • member_type (aiobungie.aiobungie.MembershipType | int): The type for the membership whose linked Destiny account you want to return.
Other Parameters
  • all (bool): If provided and set to True, All memberships regardless of whether they're obscured by overrides will be returned,

    If provided and set to False, Only available memberships will be returned. The default for this is False.

Returns
async def fetch_clan_banners(self) -> collections.abc.Mapping[str, typing.Any]:
1167    async def fetch_clan_banners(self) -> typedefs.JSONObject:
1168        resp = await self._request(_GET, "Destiny2/Clan/ClanBannerDictionary/")
1169        assert isinstance(resp, dict)
1170        return resp

Fetch the values of the clan banners.

Returns
async def fetch_public_milestones(self) -> collections.abc.Mapping[str, typing.Any]:
1172    async def fetch_public_milestones(self) -> typedefs.JSONObject:
1173        resp = await self._request(_GET, "Destiny2/Milestones/")
1174        assert isinstance(resp, dict)
1175        return resp

Fetch the available milestones.

Returns
async def fetch_public_milestone_content(self, milestone_hash: int, /) -> collections.abc.Mapping[str, typing.Any]:
1177    async def fetch_public_milestone_content(
1178        self, milestone_hash: int, /
1179    ) -> typedefs.JSONObject:
1180        resp = await self._request(
1181            _GET, f"Destiny2/Milestones/{milestone_hash}/Content/"
1182        )
1183        assert isinstance(resp, dict)
1184        return resp

Fetch the milestone content given its hash.

Parameters
  • milestone_hash (int): The milestone hash.
Returns
async def fetch_current_user_memberships(self, access_token: str, /) -> collections.abc.Mapping[str, typing.Any]:
1186    async def fetch_current_user_memberships(
1187        self, access_token: str, /
1188    ) -> typedefs.JSONObject:
1189        resp = await self._request(
1190            _GET,
1191            "User/GetMembershipsForCurrentUser/",
1192            auth=access_token,
1193        )
1194        assert isinstance(resp, dict)
1195        return resp

Fetch a bungie user's accounts with the signed in user. This GET method requires a Bearer access token for the authorization.

This requires OAuth2 scope enabled and the valid Bearer access_token.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def equip_item( self, access_token: str, /, item_id: int, character_id: int, membership_type: MembershipType | int) -> None:
1197    async def equip_item(
1198        self,
1199        access_token: str,
1200        /,
1201        item_id: int,
1202        character_id: int,
1203        membership_type: enums.MembershipType | int,
1204    ) -> None:
1205        payload = {
1206            "itemId": item_id,
1207            "characterId": character_id,
1208            "membershipType": int(membership_type),
1209        }
1210
1211        await self._request(
1212            _POST,
1213            "Destiny2/Actions/Items/EquipItem/",
1214            json=payload,
1215            auth=access_token,
1216        )

Equip an item to a character.

This requires the OAuth2: MoveEquipDestinyItems scope. Also You must have a valid Destiny account, and either be in a social space, in orbit or offline.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_id (int): The item id.
  • character_id (int): The character's id to equip the item to.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The membership type associated with this player.
async def equip_items( self, access_token: str, /, item_ids: collections.abc.Sequence[int], character_id: int, membership_type: MembershipType | int) -> None:
1218    async def equip_items(
1219        self,
1220        access_token: str,
1221        /,
1222        item_ids: collections.Sequence[int],
1223        character_id: int,
1224        membership_type: enums.MembershipType | int,
1225    ) -> None:
1226        payload = {
1227            "itemIds": item_ids,
1228            "characterId": character_id,
1229            "membershipType": int(membership_type),
1230        }
1231        await self._request(
1232            _POST,
1233            "Destiny2/Actions/Items/EquipItems/",
1234            json=payload,
1235            auth=access_token,
1236        )

Equip multiple items to a character.

This requires the OAuth2: MoveEquipDestinyItems scope. Also You must have a valid Destiny account, and either be in a social space, in orbit or offline.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_ids (Sequence[int]): A sequence of item ids.
  • character_id (int): The character's id to equip the item to.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The membership type associated with this player.
async def ban_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: MembershipType | int, *, length: int = 0, comment: str | None = None) -> None:
1238    async def ban_clan_member(
1239        self,
1240        access_token: str,
1241        /,
1242        group_id: int,
1243        membership_id: int,
1244        membership_type: enums.MembershipType | int,
1245        *,
1246        length: int = 0,
1247        comment: str | None = None,
1248    ) -> None:
1249        payload = {"comment": str(comment), "length": length}
1250        await self._request(
1251            _POST,
1252            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Ban/",
1253            json=payload,
1254            auth=access_token,
1255        )

Bans a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to ban.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The member's membership type.
Other Parameters
  • length (int): An optional ban length.
  • comment (aiobungie.UndefinedOr[str]): An optional comment to this ban. Default is UNDEFINED
async def unban_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: MembershipType | int) -> None:
1257    async def unban_clan_member(
1258        self,
1259        access_token: str,
1260        /,
1261        group_id: int,
1262        membership_id: int,
1263        membership_type: enums.MembershipType | int,
1264    ) -> None:
1265        await self._request(
1266            _POST,
1267            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Unban/",
1268            auth=access_token,
1269        )

Unban a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to unban.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The member's membership type.
async def kick_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: MembershipType | int) -> collections.abc.Mapping[str, typing.Any]:
1271    async def kick_clan_member(
1272        self,
1273        access_token: str,
1274        /,
1275        group_id: int,
1276        membership_id: int,
1277        membership_type: enums.MembershipType | int,
1278    ) -> typedefs.JSONObject:
1279        resp = await self._request(
1280            _POST,
1281            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Kick/",
1282            auth=access_token,
1283        )
1284        assert isinstance(resp, dict)
1285        return resp

Kick a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to kick.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The member's membership type.
Returns
async def edit_clan( self, access_token: str, /, group_id: int, *, name: str | None = None, about: str | None = None, motto: str | None = None, theme: str | None = None, tags: collections.abc.Sequence[str] | None = None, is_public: bool | None = None, locale: str | None = None, avatar_image_index: int | None = None, membership_option: MembershipOption | int | None = None, allow_chat: bool | None = None, chat_security: Optional[Literal[0, 1]] = None, call_sign: str | None = None, homepage: Optional[Literal[0, 1, 2]] = None, enable_invite_messaging_for_admins: bool | None = None, default_publicity: Optional[Literal[0, 1, 2]] = None, is_public_topic_admin: bool | None = None) -> None:
1287    async def edit_clan(
1288        self,
1289        access_token: str,
1290        /,
1291        group_id: int,
1292        *,
1293        name: str | None = None,
1294        about: str | None = None,
1295        motto: str | None = None,
1296        theme: str | None = None,
1297        tags: collections.Sequence[str] | None = None,
1298        is_public: bool | None = None,
1299        locale: str | None = None,
1300        avatar_image_index: int | None = None,
1301        membership_option: enums.MembershipOption | int | None = None,
1302        allow_chat: bool | None = None,
1303        chat_security: typing.Literal[0, 1] | None = None,
1304        call_sign: str | None = None,
1305        homepage: typing.Literal[0, 1, 2] | None = None,
1306        enable_invite_messaging_for_admins: bool | None = None,
1307        default_publicity: typing.Literal[0, 1, 2] | None = None,
1308        is_public_topic_admin: bool | None = None,
1309    ) -> None:
1310        payload = {
1311            "name": name,
1312            "about": about,
1313            "motto": motto,
1314            "theme": theme,
1315            "tags": tags,
1316            "isPublic": is_public,
1317            "avatarImageIndex": avatar_image_index,
1318            "isPublicTopicAdminOnly": is_public_topic_admin,
1319            "allowChat": allow_chat,
1320            "chatSecurity": chat_security,
1321            "callsign": call_sign,
1322            "homepage": homepage,
1323            "enableInvitationMessagingForAdmins": enable_invite_messaging_for_admins,
1324            "defaultPublicity": default_publicity,
1325            "locale": locale,
1326        }
1327        if membership_option is not None:
1328            payload["membershipOption"] = int(membership_option)
1329
1330        await self._request(
1331            _POST,
1332            f"GroupV2/{group_id}/Edit",
1333            json=payload,
1334            auth=access_token,
1335        )

Edit a clan.

Notes
  • This request requires OAuth2: oauth2: AdminGroups scope.
  • All arguments will default to None if not provided. This does not include access_token and group_id
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id to edit.
Other Parameters
  • name (str | None): The name to edit the clan with.
  • about (str | None): The about section to edit the clan with.
  • motto (str | None): The motto section to edit the clan with.
  • theme (str | None): The theme name to edit the clan with.
  • tags (collections.Sequence[str] | None): A sequence of strings to replace the clan tags with.
  • is_public (bool | None): If provided and set to True, The clan will set to private. If provided and set to False, The clan will set to public whether it was or not.
  • locale (str | None): The locale section to edit the clan with.
  • avatar_image_index (int | None): The clan avatar image index to edit the clan with.
  • membership_option : aiobungie.typedefs.NoneOr[aiobungie.aiobungie.MembershipOption | int] # noqa (E501 # Line too long): The clan membership option to edit it with.
  • allow_chat (bool | None): If provided and set to True, The clan members will be allowed to chat. If provided and set to False, The clan members will not be allowed to chat.
  • chat_security (aiobungie.typedefs.NoneOr[typing.Literal[0, 1]]): If provided and set to 0, The clan chat security will be edited to Group only. If provided and set to 1, The clan chat security will be edited to Admin only.
  • call_sign (str | None): The clan call sign to edit it with.
  • homepage (aiobungie.typedefs.NoneOr[typing.Literal[0, 1, 2]]): If provided and set to 0, The clan chat homepage will be edited to Wall. If provided and set to 1, The clan chat homepage will be edited to Forum. If provided and set to 0, The clan chat homepage will be edited to AllianceForum.
  • enable_invite_messaging_for_admins (bool | None): ???
  • default_publicity (aiobungie.typedefs.NoneOr[typing.Literal[0, 1, 2]]): If provided and set to 0, The clan chat publicity will be edited to Public. If provided and set to 1, The clan chat publicity will be edited to Alliance. If provided and set to 2, The clan chat publicity will be edited to Private.
  • is_public_topic_admin (bool | None): ???
async def edit_clan_options( self, access_token: str, /, group_id: int, *, invite_permissions_override: bool | None = None, update_culture_permissionOverride: bool | None = None, host_guided_game_permission_override: Optional[Literal[0, 1, 2]] = None, update_banner_permission_override: bool | None = None, join_level: ClanMemberType | int | None = None) -> None:
1337    async def edit_clan_options(
1338        self,
1339        access_token: str,
1340        /,
1341        group_id: int,
1342        *,
1343        invite_permissions_override: bool | None = None,
1344        update_culture_permissionOverride: bool | None = None,
1345        host_guided_game_permission_override: typing.Literal[0, 1, 2] | None = None,
1346        update_banner_permission_override: bool | None = None,
1347        join_level: enums.ClanMemberType | int | None = None,
1348    ) -> None:
1349        payload = {
1350            "InvitePermissionOverride": invite_permissions_override,
1351            "UpdateCulturePermissionOverride": update_culture_permissionOverride,
1352            "HostGuidedGamePermissionOverride": host_guided_game_permission_override,
1353            "UpdateBannerPermissionOverride": update_banner_permission_override,
1354            "JoinLevel": int(join_level) if join_level else None,
1355        }
1356
1357        await self._request(
1358            _POST,
1359            f"GroupV2/{group_id}/EditFounderOptions",
1360            json=payload,
1361            auth=access_token,
1362        )

Edit the clan options.

Notes
  • This request requires OAuth2: oauth2: AdminGroups scope.
  • All arguments will default to None if not provided. This does not include access_token and group_id
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
Other Parameters
  • invite_permissions_override (bool | None): Minimum Member Level allowed to invite new members to group Always Allowed: Founder, Acting Founder True means admins have this power, false means they don't Default is False for clans, True for groups.
  • update_culture_permissionOverride (bool | None): Minimum Member Level allowed to update group culture Always Allowed: Founder, Acting Founder True means admins have this power, false means they don't Default is False for clans, True for groups.
  • host_guided_game_permission_override (aiobungie.typedefs.NoneOr[typing.Literal[0, 1, 2]]): Minimum Member Level allowed to host guided games Always Allowed: Founder, Acting Founder, Admin Allowed Overrides: 0 -> None, 1 -> Beginner 2 -> Member. Default is Member for clans, None for groups, although this means nothing for groups.
  • update_banner_permission_override (bool | None): Minimum Member Level allowed to update banner Always Allowed: Founder, Acting Founder True means admins have this power, false means they don't Default is False for clans, True for groups.
  • join_level (aiobungie.ClanMemberType): Level to join a member at when accepting an invite, application, or joining an open clan. Default is aiobungie.ClanMemberType.BEGINNER
async def fetch_friends(self, access_token: str, /) -> collections.abc.Mapping[str, typing.Any]:
1364    async def fetch_friends(self, access_token: str, /) -> typedefs.JSONObject:
1365        resp = await self._request(
1366            _GET,
1367            "Social/Friends/",
1368            auth=access_token,
1369        )
1370        assert isinstance(resp, dict)
1371        return resp

Fetch bungie friend list.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def fetch_friend_requests(self, access_token: str, /) -> collections.abc.Mapping[str, typing.Any]:
1373    async def fetch_friend_requests(self, access_token: str, /) -> typedefs.JSONObject:
1374        resp = await self._request(
1375            _GET,
1376            "Social/Friends/Requests",
1377            auth=access_token,
1378        )
1379        assert isinstance(resp, dict)
1380        return resp

Fetch pending bungie friend requests queue.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def accept_friend_request(self, access_token: str, /, member_id: int) -> None:
1382    async def accept_friend_request(self, access_token: str, /, member_id: int) -> None:
1383        await self._request(
1384            _POST,
1385            f"Social/Friends/Requests/Accept/{member_id}",
1386            auth=access_token,
1387        )

Accepts a friend relationship with the target user. The user must be on your incoming friend request list.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to accept.
async def send_friend_request(self, access_token: str, /, member_id: int) -> None:
1389    async def send_friend_request(self, access_token: str, /, member_id: int) -> None:
1390        await self._request(
1391            _POST,
1392            f"Social/Friends/Add/{member_id}",
1393            auth=access_token,
1394        )

Requests a friend relationship with the target user.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to send the request to.
async def decline_friend_request(self, access_token: str, /, member_id: int) -> None:
1396    async def decline_friend_request(
1397        self, access_token: str, /, member_id: int
1398    ) -> None:
1399        await self._request(
1400            _POST,
1401            f"Social/Friends/Requests/Decline/{member_id}",
1402            auth=access_token,
1403        )

Decline a friend request with the target user. The user must be in your incoming friend request list.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to decline.
async def remove_friend(self, access_token: str, /, member_id: int) -> None:
1405    async def remove_friend(self, access_token: str, /, member_id: int) -> None:
1406        await self._request(
1407            _POST,
1408            f"Social/Friends/Remove/{member_id}",
1409            auth=access_token,
1410        )

Removes a friend from your friend list. The user must be in your friend list.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to remove.
async def remove_friend_request(self, access_token: str, /, member_id: int) -> None:
1412    async def remove_friend_request(self, access_token: str, /, member_id: int) -> None:
1413        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1414        await self._request(
1415            _POST,
1416            f"Social/Friends/Requests/Remove/{member_id}",
1417            auth=access_token,
1418        )

Removes a friend from your friend list requests. The user must be in your outgoing request list.

.. note : This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to remove from the requested friend list.
async def approve_all_pending_group_users( self, access_token: str, /, group_id: int, message: str | None = None) -> None:
1420    async def approve_all_pending_group_users(
1421        self,
1422        access_token: str,
1423        /,
1424        group_id: int,
1425        message: str | None = None,
1426    ) -> None:
1427        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1428        await self._request(
1429            _POST,
1430            f"GroupV2/{group_id}/Members/ApproveAll",
1431            auth=access_token,
1432            json={"message": str(message)},
1433        )

Approve all pending users for the given group id.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
Other Parameters
  • message (aiobungie.UndefinedOr[str]): An optional message to send with the request. Default is UNDEFINED.
async def deny_all_pending_group_users( self, access_token: str, /, group_id: int, *, message: str | None = None) -> None:
1435    async def deny_all_pending_group_users(
1436        self,
1437        access_token: str,
1438        /,
1439        group_id: int,
1440        *,
1441        message: str | None = None,
1442    ) -> None:
1443        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1444        await self._request(
1445            _POST,
1446            f"GroupV2/{group_id}/Members/DenyAll",
1447            auth=access_token,
1448            json={"message": str(message)},
1449        )

Deny all pending users for the given group id.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
Other Parameters
  • message (aiobungie.UndefinedOr[str]): An optional message to send with the request. Default is UNDEFINED.
async def add_optional_conversation( self, access_token: str, /, group_id: int, *, name: str | None = None, security: Literal[0, 1] = 0) -> None:
1451    async def add_optional_conversation(
1452        self,
1453        access_token: str,
1454        /,
1455        group_id: int,
1456        *,
1457        name: str | None = None,
1458        security: typing.Literal[0, 1] = 0,
1459    ) -> None:
1460        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1461        payload = {"chatName": str(name), "chatSecurity": security}
1462        await self._request(
1463            _POST,
1464            f"GroupV2/{group_id}/OptionalConversations/Add",
1465            json=payload,
1466            auth=access_token,
1467        )

Add a new chat channel to a group.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
Other parameters

name: aiobungie.UndefinedOr[str] The chat name. Default to UNDEFINED security: typing.Literal[0, 1] The security level of the chat.

If provided and set to 0, It will be to `Group` only.
If provided and set to 1, It will be `Admins` only.
Default is `0`
async def edit_optional_conversation( self, access_token: str, /, group_id: int, conversation_id: int, *, name: str | None = None, security: Literal[0, 1] = 0, enable_chat: bool = False) -> None:
1469    async def edit_optional_conversation(
1470        self,
1471        access_token: str,
1472        /,
1473        group_id: int,
1474        conversation_id: int,
1475        *,
1476        name: str | None = None,
1477        security: typing.Literal[0, 1] = 0,
1478        enable_chat: bool = False,
1479    ) -> None:
1480        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1481        payload = {
1482            "chatEnabled": enable_chat,
1483            "chatName": str(name),
1484            "chatSecurity": security,
1485        }
1486        await self._request(
1487            _POST,
1488            f"GroupV2/{group_id}/OptionalConversations/Edit/{conversation_id}",
1489            json=payload,
1490            auth=access_token,
1491        )

Edit the settings of this chat channel.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
  • conversation_id (int): The conversation/chat id.
Other parameters

name: aiobungie.UndefinedOr[str] The new chat name. Default to UNDEFINED security: typing.Literal[0, 1] The new security level of the chat.

If provided and set to 0, It will be to `Group` only.
If provided and set to 1, It will be `Admins` only.
Default is `0`

enable_chat : bool Whether to enable chatting or not. If set to True then chatting will be enabled. Otherwise it will be disabled.

async def transfer_item( self, access_token: str, /, item_id: int, item_hash: int, character_id: int, member_type: MembershipType | int, *, stack_size: int = 1, vault: bool = False) -> None:
1493    async def transfer_item(
1494        self,
1495        access_token: str,
1496        /,
1497        item_id: int,
1498        item_hash: int,
1499        character_id: int,
1500        member_type: enums.MembershipType | int,
1501        *,
1502        stack_size: int = 1,
1503        vault: bool = False,
1504    ) -> None:
1505        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1506        payload = {
1507            "characterId": character_id,
1508            "membershipType": int(member_type),
1509            "itemId": item_id,
1510            "itemReferenceHash": item_hash,
1511            "stackSize": stack_size,
1512            "transferToVault": vault,
1513        }
1514        await self._request(
1515            _POST,
1516            "Destiny2/Actions/Items/TransferItem",
1517            json=payload,
1518            auth=access_token,
1519        )

Transfer an item from / to your vault.

Notes
  • This method requires OAuth2: MoveEquipDestinyItems scope.
  • This method requires both item id and hash.
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_id (int): The item instance id you to transfer.
  • item_hash (int): The item hash.
  • character_id (int): The character id to transfer the item from/to.
  • member_type (aiobungie.aiobungie.MembershipType | int): The user membership type.
Other Parameters
  • stack_size (int): The item stack size.
  • vault (bool): Whether to transfer this item to your vault or not. Defaults to False.
async def pull_item( self, access_token: str, /, item_id: int, item_hash: int, character_id: int, member_type: MembershipType | int, *, stack_size: int = 1, vault: bool = False) -> None:
1521    async def pull_item(
1522        self,
1523        access_token: str,
1524        /,
1525        item_id: int,
1526        item_hash: int,
1527        character_id: int,
1528        member_type: enums.MembershipType | int,
1529        *,
1530        stack_size: int = 1,
1531        vault: bool = False,
1532    ) -> None:
1533        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1534        payload = {
1535            "characterId": character_id,
1536            "membershipType": int(member_type),
1537            "itemId": item_id,
1538            "itemReferenceHash": item_hash,
1539            "stackSize": stack_size,
1540            "transferToVault": vault,
1541        }
1542        await self._request(
1543            _POST,
1544            "Destiny2/Actions/Items/PullFromPostmaster",
1545            json=payload,
1546            auth=access_token,
1547        )

pull an item from the postmaster.

Notes
  • This method requires OAuth2: MoveEquipDestinyItems scope.
  • This method requires both item id and hash.
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_id (int): The item instance id to pull.
  • item_hash (int): The item hash.
  • character_id (int): The character id to pull the item to.
  • member_type (aiobungie.aiobungie.MembershipType | int): The user membership type.
Other Parameters
  • stack_size (int): The item stack size.
  • vault (bool): Whether to pill this item to your vault or not. Defaults to False.
async def fetch_fireteams( self, activity_type: FireteamActivity | int, *, platform: FireteamPlatform | int = <FireteamPlatform.ANY: 0>, language: FireteamLanguage | str = <FireteamLanguage.ALL: >, date_range: FireteamDate | int = <FireteamDate.ALL: 0>, page: int = 0, slots_filter: int = 0) -> collections.abc.Mapping[str, typing.Any]:
1549    async def fetch_fireteams(
1550        self,
1551        activity_type: fireteams.FireteamActivity | int,
1552        *,
1553        platform: fireteams.FireteamPlatform | int = fireteams.FireteamPlatform.ANY,
1554        language: fireteams.FireteamLanguage | str = fireteams.FireteamLanguage.ALL,
1555        date_range: fireteams.FireteamDate | int = fireteams.FireteamDate.ALL,
1556        page: int = 0,
1557        slots_filter: int = 0,
1558    ) -> typedefs.JSONObject:
1559        resp = await self._request(
1560            _GET,
1561            f"Fireteam/Search/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{page}/?langFilter={str(language)}",  # noqa: E501 Line too long
1562        )
1563        assert isinstance(resp, dict)
1564        return resp

Fetch public Bungie fireteams with open slots.

Parameters
  • activity_type (aiobungie.aiobungie.crates.FireteamActivity | int): The fireteam activity type.
Other Parameters
  • platform (aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (FireteamLanguage | str): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • date_range (aiobungie.aiobungie.FireteamDate | int): An integer to filter the date range of the returned fireteams. Defaults to aiobungie.FireteamDate.ALL.
  • page (int): The page number. By default its 0 which returns all available activities.
  • slots_filter (int): Filter the returned fireteams based on available slots. Default is 0
Returns
async def fetch_available_clan_fireteams( self, access_token: str, group_id: int, activity_type: FireteamActivity | int, *, platform: FireteamPlatform | int, language: FireteamLanguage | str, date_range: FireteamDate | int = <FireteamDate.ALL: 0>, page: int = 0, public_only: bool = False, slots_filter: int = 0) -> collections.abc.Mapping[str, typing.Any]:
1566    async def fetch_available_clan_fireteams(
1567        self,
1568        access_token: str,
1569        group_id: int,
1570        activity_type: fireteams.FireteamActivity | int,
1571        *,
1572        platform: fireteams.FireteamPlatform | int,
1573        language: fireteams.FireteamLanguage | str,
1574        date_range: fireteams.FireteamDate | int = fireteams.FireteamDate.ALL,
1575        page: int = 0,
1576        public_only: bool = False,
1577        slots_filter: int = 0,
1578    ) -> typedefs.JSONObject:
1579        resp = await self._request(
1580            _GET,
1581            f"Fireteam/Clan/{group_id}/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{public_only}/{page}",  # noqa: E501
1582            json={"langFilter": str(language)},
1583            auth=access_token,
1584        )
1585        assert isinstance(resp, dict)
1586        return resp

Fetch a clan's fireteams with open slots.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id of the fireteam.
  • activity_type (aiobungie.aiobungie.crates.FireteamActivity | int): The fireteam activity type.
Other Parameters
  • platform (aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (FireteamLanguage | str): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • date_range (aiobungie.aiobungie.FireteamDate | int): An integer to filter the date range of the returned fireteams. Defaults to aiobungie.FireteamDate.ALL.
  • page (int): The page number. By default its 0 which returns all available activities.
  • public_only (bool): If set to True, Then only public fireteams will be returned.
  • slots_filter (int): Filter the returned fireteams based on available slots. Default is 0
Returns
async def fetch_clan_fireteam( self, access_token: str, fireteam_id: int, group_id: int) -> collections.abc.Mapping[str, typing.Any]:
1588    async def fetch_clan_fireteam(
1589        self, access_token: str, fireteam_id: int, group_id: int
1590    ) -> typedefs.JSONObject:
1591        resp = await self._request(
1592            _GET,
1593            f"Fireteam/Clan/{group_id}/Summary/{fireteam_id}",
1594            auth=access_token,
1595        )
1596        assert isinstance(resp, dict)
1597        return resp

Fetch a specific clan fireteam.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch the fireteam from.
  • fireteam_id (int): The fireteam id to fetch.
Returns
async def fetch_my_clan_fireteams( self, access_token: str, group_id: int, *, include_closed: bool = True, platform: FireteamPlatform | int, language: FireteamLanguage | str, filtered: bool = True, page: int = 0) -> collections.abc.Mapping[str, typing.Any]:
1599    async def fetch_my_clan_fireteams(
1600        self,
1601        access_token: str,
1602        group_id: int,
1603        *,
1604        include_closed: bool = True,
1605        platform: fireteams.FireteamPlatform | int,
1606        language: fireteams.FireteamLanguage | str,
1607        filtered: bool = True,
1608        page: int = 0,
1609    ) -> typedefs.JSONObject:
1610        payload = {"groupFilter": filtered, "langFilter": str(language)}
1611
1612        resp = await self._request(
1613            _GET,
1614            f"Fireteam/Clan/{group_id}/My/{int(platform)}/{include_closed}/{page}",
1615            json=payload,
1616            auth=access_token,
1617        )
1618        assert isinstance(resp, dict)
1619        return resp

Fetch a clan's fireteams with open slots.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch.
Other Parameters
  • include_closed (bool): If provided and set to True, It will also return closed fireteams. If provided and set to False, It will only return public fireteams. Default is True.
  • platform (aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (FireteamLanguage | str): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • filtered (bool): If set to True, it will filter by clan. Otherwise not. Default is True.
  • page (int): The page number. By default its 0 which returns all available activities.
Returns
async def fetch_private_clan_fireteams(self, access_token: str, group_id: int, /) -> int:
1621    async def fetch_private_clan_fireteams(
1622        self, access_token: str, group_id: int, /
1623    ) -> int:
1624        resp = await self._request(
1625            _GET,
1626            f"Fireteam/Clan/{group_id}/ActiveCount",
1627            auth=access_token,
1628        )
1629        assert isinstance(resp, int)
1630        return resp

Fetch the active count of the clan fireteams that are only private.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id.
Returns
  • int: The active fireteams count. Max value returned is 25.
async def fetch_post_activity(self, instance_id: int, /) -> collections.abc.Mapping[str, typing.Any]:
1632    async def fetch_post_activity(self, instance_id: int, /) -> typedefs.JSONObject:
1633        resp = await self._request(
1634            _GET, f"Destiny2/Stats/PostGameCarnageReport/{instance_id}"
1635        )
1636        assert isinstance(resp, dict)
1637        return resp

Fetch a post activity details.

Parameters
  • instance_id (int): The activity instance id.
Returns
async def search_entities( self, name: str, entity_type: str, *, page: int = 0) -> collections.abc.Mapping[str, typing.Any]:
1639    async def search_entities(
1640        self, name: str, entity_type: str, *, page: int = 0
1641    ) -> typedefs.JSONObject:
1642        resp = await self._request(
1643            _GET,
1644            f"Destiny2/Armory/Search/{entity_type}/{name}/",
1645            json={"page": page},
1646        )
1647        assert isinstance(resp, dict)
1648        return resp

Search for Destiny2 entities given a name and its type.

Parameters
  • name (str): The name of the entity, i.e., Thunderlord, One thousand voices.
  • entity_type (str): The type of the entity, AKA Definition, For an example DestinyInventoryItemDefinition
Other Parameters
  • page (int): An optional page to return. Default to 0.
Returns
async def fetch_unique_weapon_history( self, membership_id: int, character_id: int, membership_type: MembershipType | int) -> collections.abc.Mapping[str, typing.Any]:
1650    async def fetch_unique_weapon_history(
1651        self,
1652        membership_id: int,
1653        character_id: int,
1654        membership_type: enums.MembershipType | int,
1655    ) -> typedefs.JSONObject:
1656        resp = await self._request(
1657            _GET,
1658            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/UniqueWeapons/",
1659        )
1660        assert isinstance(resp, dict)
1661        return resp

Fetch details about unique weapon usage for a character. Includes all exotics.

Parameters
  • membership_id (int): The Destiny user membership id.
  • character_id (int): The character id to retrieve.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The Destiny user's membership type.
Returns
async def fetch_item( self, member_id: int, item_id: int, membership_type: MembershipType | int, components: collections.abc.Sequence[ComponentType]) -> collections.abc.Mapping[str, typing.Any]:
1663    async def fetch_item(
1664        self,
1665        member_id: int,
1666        item_id: int,
1667        membership_type: enums.MembershipType | int,
1668        components: collections.Sequence[enums.ComponentType],
1669    ) -> typedefs.JSONObject:
1670        collector = _collect_components(components)
1671
1672        resp = await self._request(
1673            _GET,
1674            f"Destiny2/{int(membership_type)}/Profile/{member_id}/Item/{item_id}/?components={collector}",
1675        )
1676        assert isinstance(resp, dict)
1677        return resp

Fetch an instanced Destiny 2 item's details.

Parameters
  • member_id (int): The membership id of the Destiny 2 player.
  • item_id (int): The instance id of the item.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The membership type of the Destiny 2 player.
  • components (collections.Sequence[aiobungie.ComponentType]): A list of components to retrieve.
Returns
async def fetch_clan_weekly_rewards(self, clan_id: int, /) -> collections.abc.Mapping[str, typing.Any]:
1679    async def fetch_clan_weekly_rewards(self, clan_id: int, /) -> typedefs.JSONObject:
1680        resp = await self._request(_GET, f"Destiny2/Clan/{clan_id}/WeeklyRewardState/")
1681        assert isinstance(resp, dict)
1682        return resp

Fetch the weekly reward state for a clan.

Parameters
  • clan_id (int): The clan id.
Returns
async def fetch_available_locales(self) -> collections.abc.Mapping[str, typing.Any]:
1684    async def fetch_available_locales(self) -> typedefs.JSONObject:
1685        resp = await self._request(_GET, "Destiny2/Manifest/DestinyLocaleDefinition/")
1686        assert isinstance(resp, dict)
1687        return resp

Fetch available locales at Bungie.

Returns
async def fetch_common_settings(self) -> collections.abc.Mapping[str, typing.Any]:
1689    async def fetch_common_settings(self) -> typedefs.JSONObject:
1690        resp = await self._request(_GET, "Settings")
1691        assert isinstance(resp, dict)
1692        return resp

Fetch the common settings used by Bungie's environment.

Returns
async def fetch_user_systems_overrides(self) -> collections.abc.Mapping[str, typing.Any]:
1694    async def fetch_user_systems_overrides(self) -> typedefs.JSONObject:
1695        resp = await self._request(_GET, "UserSystemOverrides")
1696        assert isinstance(resp, dict)
1697        return resp

Fetch a user's specific system overrides.

Returns
async def fetch_global_alerts( self, *, include_streaming: bool = False) -> collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]]:
1699    async def fetch_global_alerts(
1700        self, *, include_streaming: bool = False
1701    ) -> typedefs.JSONArray:
1702        resp = await self._request(
1703            _GET, f"GlobalAlerts/?includestreaming={include_streaming}"
1704        )
1705        assert isinstance(resp, list)
1706        return resp

Fetch any active global alerts.

Parameters
  • include_streaming (bool): If True, the returned results will include streaming alerts. Default is False.
Returns
async def awainitialize_request( self, access_token: str, type: Literal[0, 1], membership_type: MembershipType | int, /, *, affected_item_id: int | None = None, character_id: int | None = None) -> collections.abc.Mapping[str, typing.Any]:
1708    async def awainitialize_request(
1709        self,
1710        access_token: str,
1711        type: typing.Literal[0, 1],
1712        membership_type: enums.MembershipType | int,
1713        /,
1714        *,
1715        affected_item_id: int | None = None,
1716        character_id: int | None = None,
1717    ) -> typedefs.JSONObject:
1718        body = {"type": type, "membershipType": int(membership_type)}
1719
1720        if affected_item_id is not None:
1721            body["affectedItemId"] = affected_item_id
1722
1723        if character_id is not None:
1724            body["characterId"] = character_id
1725
1726        resp = await self._request(
1727            _POST, "Destiny2/Awa/Initialize", json=body, auth=access_token
1728        )
1729        assert isinstance(resp, dict)
1730        return resp

Initialize a request to perform an advanced write action.

OAuth2: AdvancedWriteActions application scope is required to perform this request.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • type (typing.Literal[0, 1]): Type of the advanced write action. Its either 0 or 1. If set to 0 that means it None. Otherwise if 1 that means its insert plugs.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The Destiny membership type of the account to modify.
Other Parameters
  • affected_item_id (int | None): Item instance ID the action shall be applied to. This is optional for all but a new AwaType values.
  • character_id (int | None): The Destiny character ID to perform this action on.
Returns
async def awaget_action_token( self, access_token: str, correlation_id: str, /) -> collections.abc.Mapping[str, typing.Any]:
1732    async def awaget_action_token(
1733        self, access_token: str, correlation_id: str, /
1734    ) -> typedefs.JSONObject:
1735        resp = await self._request(
1736            _POST,
1737            f"Destiny2/Awa/GetActionToken/{correlation_id}",
1738            auth=access_token,
1739        )
1740        assert isinstance(resp, dict)
1741        return resp

Returns the action token if user approves the request.

OAuth2: AdvancedWriteActions application scope is required to perform this request.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • correlation_id (str): The identifier for the advanced write action request.
Returns
async def awa_provide_authorization_result( self, access_token: str, selection: int, correlation_id: str, nonce: collections.abc.MutableSequence[str | bytes]) -> int:
1743    async def awa_provide_authorization_result(
1744        self,
1745        access_token: str,
1746        selection: int,
1747        correlation_id: str,
1748        nonce: collections.MutableSequence[str | bytes],
1749    ) -> int:
1750        body = {"selection": selection, "correlationId": correlation_id, "nonce": nonce}
1751
1752        resp = await self._request(
1753            _POST,
1754            "Destiny2/Awa/AwaProvideAuthorizationResult",
1755            json=body,
1756            auth=access_token,
1757        )
1758        assert isinstance(resp, int)
1759        return resp

Provide the result of the user interaction. Called by the Bungie Destiny App to approve or reject a request.

OAuth2: AdvancedWriteActions application scope is required to perform this request.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • selection (int): Indication of the selection the user has made (Approving or rejecting the action)
  • correlation_id (str): Correlation ID of the request.
  • nonce (collections.MutableSequence[str | bytes]): Secret nonce received via the PUSH notification.
Returns
  • int: ...
async def fetch_vendors( self, access_token: str, character_id: int, membership_id: int, membership_type: MembershipType | int, /, components: collections.abc.Sequence[ComponentType], filter: int | None = None) -> collections.abc.Mapping[str, typing.Any]:
1761    async def fetch_vendors(
1762        self,
1763        access_token: str,
1764        character_id: int,
1765        membership_id: int,
1766        membership_type: enums.MembershipType | int,
1767        /,
1768        components: collections.Sequence[enums.ComponentType],
1769        filter: int | None = None,
1770    ) -> typedefs.JSONObject:
1771        components_ = _collect_components(components)
1772        route = (
1773            f"Destiny2/{int(membership_type)}/Profile/{membership_id}"
1774            f"/Character/{character_id}/Vendors/?components={components_}"
1775        )
1776
1777        if filter is not None:
1778            route = route + f"&filter={filter}"
1779
1780        resp = await self._request(
1781            _GET,
1782            route,
1783            auth=access_token,
1784        )
1785        assert isinstance(resp, dict)
1786        return resp

Get currently available vendors from the list of vendors that can possibly have rotating inventory.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • character_id (int): The character ID to return the vendor info for.
  • membership_id (int): The Destiny membership id to return the vendor info for.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The Destiny membership type to return the vendor info for.
  • components (collections.Sequence[aiobungie.ComponentType]): A list of vendor components to collect and return.
Other Parameters
  • filter (int): Filters the type of items returned from the vendor. This can be left to None.
Returns
async def fetch_vendor( self, access_token: str, character_id: int, membership_id: int, membership_type: MembershipType | int, vendor_hash: int, /, components: collections.abc.Sequence[ComponentType]) -> collections.abc.Mapping[str, typing.Any]:
1788    async def fetch_vendor(
1789        self,
1790        access_token: str,
1791        character_id: int,
1792        membership_id: int,
1793        membership_type: enums.MembershipType | int,
1794        vendor_hash: int,
1795        /,
1796        components: collections.Sequence[enums.ComponentType],
1797    ) -> typedefs.JSONObject:
1798        components_ = _collect_components(components)
1799        resp = await self._request(
1800            _GET,
1801            (
1802                f"Destiny2/{int(membership_type)}/Profile/{membership_id}"
1803                f"/Character/{character_id}/Vendors/{vendor_hash}/?components={components_}"
1804            ),
1805            auth=access_token,
1806        )
1807        assert isinstance(resp, dict)
1808        return resp

Fetch details for a specific vendor.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • character_id (int): The character ID to return the vendor info for.
  • membership_id (int): The Destiny membership id to return the vendor info for.
  • membership_type (aiobungie.aiobungie.MembershipType | int): The Destiny membership type to return the vendor info for.
  • vendor_hash (int): The vendor hash to return the details for.
  • components (collections.Sequence[aiobungie.ComponentType]): A list of vendor components to collect and return.
Returns
async def fetch_application_api_usage( self, access_token: str, application_id: int, /, *, start: datetime.datetime | None = None, end: datetime.datetime | None = None) -> collections.abc.Mapping[str, typing.Any]:
1810    async def fetch_application_api_usage(
1811        self,
1812        access_token: str,
1813        application_id: int,
1814        /,
1815        *,
1816        start: datetime.datetime | None = None,
1817        end: datetime.datetime | None = None,
1818    ) -> typedefs.JSONObject:
1819        end_date, start_date = time.parse_date_range(end, start)
1820        resp = await self._request(
1821            _GET,
1822            f"App/ApiUsage/{application_id}/?end={end_date}&start={start_date}",
1823            auth=access_token,
1824        )
1825        assert isinstance(resp, dict)
1826        return resp

Fetch a Bungie application's API usage.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • application_id (int): The application id to get.
Other Parameters
  • start (datetime.datetime | None): A datetime object can be used to collect the start of the application usage. This is limited and can go back to 30 days maximum.

    If this is left to None. It will return the last 24 hours.

  • end (datetime.datetime | None): A datetime object can be used to collect the end of the application usage.

    If this is left to None. It will return now.

Example
import datetime

# Fetch data from 2021 Dec 10th to 2021 Dec 20th
await fetch_application_api_usage(
    start=datetime.datetime(2021, 12, 10),
    end=datetime.datetime(2021, 12, 20)
)
Returns
async def fetch_bungie_applications( self) -> collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]]:
1828    async def fetch_bungie_applications(self) -> typedefs.JSONArray:
1829        resp = await self._request(_GET, "App/FirstParty")
1830        assert isinstance(resp, list)
1831        return resp

Fetch details for applications created by Bungie.

Returns
async def fetch_content_type(self, type: str, /) -> collections.abc.Mapping[str, typing.Any]:
1833    async def fetch_content_type(self, type: str, /) -> typedefs.JSONObject:
1834        resp = await self._request(_GET, f"Content/GetContentType/{type}/")
1835        assert isinstance(resp, dict)
1836        return resp
async def fetch_content_by_id( self, id: int, locale: str, /, *, head: bool = False) -> collections.abc.Mapping[str, typing.Any]:
1838    async def fetch_content_by_id(
1839        self, id: int, locale: str, /, *, head: bool = False
1840    ) -> typedefs.JSONObject:
1841        resp = await self._request(
1842            _GET,
1843            f"Content/GetContentById/{id}/{locale}/",
1844            json={"head": head},
1845        )
1846        assert isinstance(resp, dict)
1847        return resp
async def fetch_content_by_tag_and_type( self, locale: str, tag: str, type: str, *, head: bool = False) -> collections.abc.Mapping[str, typing.Any]:
1849    async def fetch_content_by_tag_and_type(
1850        self, locale: str, tag: str, type: str, *, head: bool = False
1851    ) -> typedefs.JSONObject:
1852        resp = await self._request(
1853            _GET,
1854            f"Content/GetContentByTagAndType/{tag}/{type}/{locale}/",
1855            json={"head": head},
1856        )
1857        assert isinstance(resp, dict)
1858        return resp
async def search_content_with_text( self, locale: str, /, content_type: str, search_text: str, tag: str, *, page: int | None = None, source: str | None = None) -> collections.abc.Mapping[str, typing.Any]:
1860    async def search_content_with_text(
1861        self,
1862        locale: str,
1863        /,
1864        content_type: str,
1865        search_text: str,
1866        tag: str,
1867        *,
1868        page: int | None = None,
1869        source: str | None = None,
1870    ) -> typedefs.JSONObject:
1871        body: typedefs.JSONObject = {
1872            "locale": locale,
1873            "currentpage": page or 1,
1874            "ctype": content_type,
1875            "searchtxt": search_text,
1876            "searchtext": search_text,
1877            "tag": tag,
1878            "source": source,
1879        }
1880
1881        resp = await self._request(_GET, "Content/Search", params=body)
1882        assert isinstance(resp, dict)
1883        return resp
async def search_content_by_tag_and_type( self, locale: str, tag: str, type: str, *, page: int | None = None) -> collections.abc.Mapping[str, typing.Any]:
1885    async def search_content_by_tag_and_type(
1886        self,
1887        locale: str,
1888        tag: str,
1889        type: str,
1890        *,
1891        page: int | None = None,
1892    ) -> typedefs.JSONObject:
1893        body: typedefs.JSONObject = {"currentpage": page or 1}
1894
1895        resp = await self._request(
1896            _GET,
1897            f"Content/SearchContentByTagAndType/{tag}/{type}/{locale}/",
1898            params=body,
1899        )
1900        assert isinstance(resp, dict)
1901        return resp
async def search_help_articles( self, text: str, size: str, /) -> collections.abc.Mapping[str, typing.Any]:
1903    async def search_help_articles(
1904        self, text: str, size: str, /
1905    ) -> typedefs.JSONObject:
1906        resp = await self._request(_GET, f"Content/SearchHelpArticles/{text}/{size}/")
1907        assert isinstance(resp, dict)
1908        return resp
async def fetch_topics_page( self, category_filter: int, group: int, date_filter: int, sort: str | bytes, *, page: int | None = None, locales: collections.abc.Iterable[str] | None = None, tag_filter: str | None = None) -> collections.abc.Mapping[str, typing.Any]:
1910    async def fetch_topics_page(
1911        self,
1912        category_filter: int,
1913        group: int,
1914        date_filter: int,
1915        sort: str | bytes,
1916        *,
1917        page: int | None = None,
1918        locales: collections.Iterable[str] | None = None,
1919        tag_filter: str | None = None,
1920    ) -> typedefs.JSONObject:
1921        params = {
1922            "locales": ",".join(locales) if locales is not None else "en",
1923        }
1924        if tag_filter:
1925            params["tagstring"] = tag_filter
1926
1927        resp = await self._request(
1928            _GET,
1929            f"Forum/GetTopicsPaged/{page or 0}/0/{group}/{sort!s}/{date_filter}/{category_filter}/",
1930            params=params,
1931        )
1932        assert isinstance(resp, dict)
1933        return resp
async def fetch_core_topics_page( self, category_filter: int, date_filter: int, sort: str | bytes, *, page: int | None = None, locales: collections.abc.Iterable[str] | None = None) -> collections.abc.Mapping[str, typing.Any]:
1935    async def fetch_core_topics_page(
1936        self,
1937        category_filter: int,
1938        date_filter: int,
1939        sort: str | bytes,
1940        *,
1941        page: int | None = None,
1942        locales: collections.Iterable[str] | None = None,
1943    ) -> typedefs.JSONObject:
1944        resp = await self._request(
1945            _GET,
1946            f"Forum/GetCoreTopicsPaged/{page or 0}"
1947            f"/{sort!s}/{date_filter}/{category_filter}/?locales={','.join(locales) if locales else 'en'}",
1948        )
1949        assert isinstance(resp, dict)
1950        return resp
async def fetch_posts_threaded_page( self, parent_post: bool, page: int, page_size: int, parent_post_id: int, reply_size: int, root_thread_mode: bool, sort_mode: int, show_banned: str | None = None) -> collections.abc.Mapping[str, typing.Any]:
1952    async def fetch_posts_threaded_page(
1953        self,
1954        parent_post: bool,
1955        page: int,
1956        page_size: int,
1957        parent_post_id: int,
1958        reply_size: int,
1959        root_thread_mode: bool,
1960        sort_mode: int,
1961        show_banned: str | None = None,
1962    ) -> typedefs.JSONObject:
1963        resp = await self._request(
1964            _GET,
1965            f"Forum/GetPostsThreadedPaged/{parent_post}/{page}/"
1966            f"{page_size}/{reply_size}/{parent_post_id}/{root_thread_mode}/{sort_mode}/",
1967            json={"showbanned": show_banned},
1968        )
1969        assert isinstance(resp, dict)
1970        return resp
async def fetch_posts_threaded_page_from_child( self, child_id: bool, page: int, page_size: int, reply_size: int, root_thread_mode: bool, sort_mode: int, show_banned: str | None = None) -> collections.abc.Mapping[str, typing.Any]:
1972    async def fetch_posts_threaded_page_from_child(
1973        self,
1974        child_id: bool,
1975        page: int,
1976        page_size: int,
1977        reply_size: int,
1978        root_thread_mode: bool,
1979        sort_mode: int,
1980        show_banned: str | None = None,
1981    ) -> typedefs.JSONObject:
1982        resp = await self._request(
1983            _GET,
1984            f"Forum/GetPostsThreadedPagedFromChild/{child_id}/"
1985            f"{page}/{page_size}/{reply_size}/{root_thread_mode}/{sort_mode}/",
1986            json={"showbanned": show_banned},
1987        )
1988        assert isinstance(resp, dict)
1989        return resp
async def fetch_post_and_parent( self, child_id: int, /, *, show_banned: str | None = None) -> collections.abc.Mapping[str, typing.Any]:
1991    async def fetch_post_and_parent(
1992        self, child_id: int, /, *, show_banned: str | None = None
1993    ) -> typedefs.JSONObject:
1994        resp = await self._request(
1995            _GET,
1996            f"Forum/GetPostAndParent/{child_id}/",
1997            json={"showbanned": show_banned},
1998        )
1999        assert isinstance(resp, dict)
2000        return resp
async def fetch_posts_and_parent_awaiting( self, child_id: int, /, *, show_banned: str | None = None) -> collections.abc.Mapping[str, typing.Any]:
2002    async def fetch_posts_and_parent_awaiting(
2003        self, child_id: int, /, *, show_banned: str | None = None
2004    ) -> typedefs.JSONObject:
2005        resp = await self._request(
2006            _GET,
2007            f"Forum/GetPostAndParentAwaitingApproval/{child_id}/",
2008            json={"showbanned": show_banned},
2009        )
2010        assert isinstance(resp, dict)
2011        return resp
async def fetch_topic_for_content(self, content_id: int, /) -> int:
2013    async def fetch_topic_for_content(self, content_id: int, /) -> int:
2014        resp = await self._request(_GET, f"Forum/GetTopicForContent/{content_id}/")
2015        assert isinstance(resp, int)
2016        return resp
async def fetch_forum_tag_suggestions(self, partial_tag: str, /) -> collections.abc.Mapping[str, typing.Any]:
2018    async def fetch_forum_tag_suggestions(
2019        self, partial_tag: str, /
2020    ) -> typedefs.JSONObject:
2021        resp = await self._request(
2022            _GET,
2023            "Forum/GetForumTagSuggestions/",
2024            json={"partialtag": partial_tag},
2025        )
2026        assert isinstance(resp, dict)
2027        return resp
async def fetch_poll(self, topic_id: int, /) -> collections.abc.Mapping[str, typing.Any]:
2029    async def fetch_poll(self, topic_id: int, /) -> typedefs.JSONObject:
2030        resp = await self._request(_GET, f"Forum/Poll/{topic_id}/")
2031        assert isinstance(resp, dict)
2032        return resp
async def fetch_recruitment_thread_summaries( self) -> collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]]:
2034    async def fetch_recruitment_thread_summaries(self) -> typedefs.JSONArray:
2035        resp = await self._request(_POST, "Forum/Recruit/Summaries/")
2036        assert isinstance(resp, list)
2037        return resp
async def fetch_available_avatars(self) -> collections.abc.Mapping[str, int]:
2055    async def fetch_available_avatars(self) -> collections.Mapping[str, int]:
2056        resp = await self._request(_GET, "GroupV2/GetAvailableAvatars/")
2057        assert isinstance(resp, dict)
2058        return resp
async def fetch_user_clan_invite_setting( self, access_token: str, /, membership_type: MembershipType | int) -> bool:
2060    async def fetch_user_clan_invite_setting(
2061        self,
2062        access_token: str,
2063        /,
2064        membership_type: enums.MembershipType | int,
2065    ) -> bool:
2066        resp = await self._request(
2067            _GET,
2068            f"GroupV2/GetUserClanInviteSetting/{int(membership_type)}/",
2069            auth=access_token,
2070        )
2071        assert isinstance(resp, bool)
2072        return resp
async def fetch_banned_group_members( self, access_token: str, group_id: int, /, *, page: int = 1) -> collections.abc.Mapping[str, typing.Any]:
2074    async def fetch_banned_group_members(
2075        self, access_token: str, group_id: int, /, *, page: int = 1
2076    ) -> typedefs.JSONObject:
2077        resp = await self._request(
2078            _GET,
2079            f"GroupV2/{group_id}/Banned/?currentpage={page}",
2080            auth=access_token,
2081        )
2082        assert isinstance(resp, dict)
2083        return resp
async def fetch_pending_group_memberships( self, access_token: str, group_id: int, /, *, current_page: int = 1) -> collections.abc.Mapping[str, typing.Any]:
2085    async def fetch_pending_group_memberships(
2086        self, access_token: str, group_id: int, /, *, current_page: int = 1
2087    ) -> typedefs.JSONObject:
2088        resp = await self._request(
2089            _GET,
2090            f"GroupV2/{group_id}/Members/Pending/?currentpage={current_page}",
2091            auth=access_token,
2092        )
2093        assert isinstance(resp, dict)
2094        return resp
async def fetch_invited_group_memberships( self, access_token: str, group_id: int, /, *, current_page: int = 1) -> collections.abc.Mapping[str, typing.Any]:
2096    async def fetch_invited_group_memberships(
2097        self, access_token: str, group_id: int, /, *, current_page: int = 1
2098    ) -> typedefs.JSONObject:
2099        resp = await self._request(
2100            _GET,
2101            f"GroupV2/{group_id}/Members/InvitedIndividuals/?currentpage={current_page}",
2102            auth=access_token,
2103        )
2104        assert isinstance(resp, dict)
2105        return resp
async def invite_member_to_group( self, access_token: str, /, group_id: int, membership_id: int, membership_type: MembershipType | int, *, message: str | None = None) -> collections.abc.Mapping[str, typing.Any]:
2107    async def invite_member_to_group(
2108        self,
2109        access_token: str,
2110        /,
2111        group_id: int,
2112        membership_id: int,
2113        membership_type: enums.MembershipType | int,
2114        *,
2115        message: str | None = None,
2116    ) -> typedefs.JSONObject:
2117        resp = await self._request(
2118            _POST,
2119            f"GroupV2/{group_id}/Members/IndividualInvite/{int(membership_type)}/{membership_id}/",
2120            auth=access_token,
2121            json={"message": str(message)},
2122        )
2123        assert isinstance(resp, dict)
2124        return resp
async def cancel_group_member_invite( self, access_token: str, /, group_id: int, membership_id: int, membership_type: MembershipType | int) -> collections.abc.Mapping[str, typing.Any]:
2126    async def cancel_group_member_invite(
2127        self,
2128        access_token: str,
2129        /,
2130        group_id: int,
2131        membership_id: int,
2132        membership_type: enums.MembershipType | int,
2133    ) -> typedefs.JSONObject:
2134        resp = await self._request(
2135            _POST,
2136            f"GroupV2/{group_id}/Members/IndividualInviteCancel/{int(membership_type)}/{membership_id}/",
2137            auth=access_token,
2138        )
2139        assert isinstance(resp, dict)
2140        return resp
async def fetch_historical_definition(self) -> collections.abc.Mapping[str, typing.Any]:
2142    async def fetch_historical_definition(self) -> typedefs.JSONObject:
2143        resp = await self._request(_GET, "Destiny2/Stats/Definition/")
2144        assert isinstance(resp, dict)
2145        return resp
async def fetch_historical_stats( self, character_id: int, membership_id: int, membership_type: MembershipType | int, day_start: datetime.datetime, day_end: datetime.datetime, groups: collections.abc.Sequence[aiobungie.internal.enums.StatsGroupType | int], modes: collections.abc.Sequence[GameMode | int], *, period_type: aiobungie.internal.enums.PeriodType = <PeriodType.ALL_TIME: 2>) -> collections.abc.Mapping[str, typing.Any]:
2147    async def fetch_historical_stats(
2148        self,
2149        character_id: int,
2150        membership_id: int,
2151        membership_type: enums.MembershipType | int,
2152        day_start: datetime.datetime,
2153        day_end: datetime.datetime,
2154        groups: collections.Sequence[enums.StatsGroupType | int],
2155        modes: collections.Sequence[enums.GameMode | int],
2156        *,
2157        period_type: enums.PeriodType = enums.PeriodType.ALL_TIME,
2158    ) -> typedefs.JSONObject:
2159        end, start = time.parse_date_range(day_end, day_start)
2160        resp = await self._request(
2161            _GET,
2162            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/",
2163            json={
2164                "dayend": end,
2165                "daystart": start,
2166                "groups": [str(int(group)) for group in groups],
2167                "modes": [str(int(mode)) for mode in modes],
2168                "periodType": int(period_type),
2169            },
2170        )
2171        assert isinstance(resp, dict)
2172        return resp

Fetch historical stats for a specific membership character.

Parameters
  • character_id (int): The character ID to return the stats for.
  • membership_id (int): The Destiny membership id to return the stats for.
  • membership_type (aiobungie.MembershipType | int): The Destiny membership type to return the stats for.
  • day_start (datetime.datetime): The start of the day to return the stats for.
  • day_end (datetime.datetime): The end of the day to return the stats for.
  • groups (collections.Sequence[aiobungie.StatsGroupType]): A list of stats groups to return.
  • modes (collections.Sequence[aiobungie.GameMode | int]): A list of game modes to return.
  • period_type (aiobungie.enums.PeriodType): The period type to return the stats for. This will return ALL_TIME by default if not modified.
Returns
async def fetch_historical_stats_for_account( self, membership_id: int, membership_type: MembershipType | int, groups: collections.abc.Sequence[aiobungie.internal.enums.StatsGroupType | int]) -> collections.abc.Mapping[str, typing.Any]:
2174    async def fetch_historical_stats_for_account(
2175        self,
2176        membership_id: int,
2177        membership_type: enums.MembershipType | int,
2178        groups: collections.Sequence[enums.StatsGroupType | int],
2179    ) -> typedefs.JSONObject:
2180        resp = await self._request(
2181            _GET,
2182            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Stats/",
2183            json={"groups": [str(int(group)) for group in groups]},
2184        )
2185        assert isinstance(resp, dict)
2186        return resp

Fetch historical stats for an account's membership.

Parameters
  • membership_id (int): The Destiny membership id to return the stats for.
  • membership_type (aiobungie.MembershipType | int): The Destiny membership type to return the stats for.
  • groups (collections.Sequence[aiobungie.StatsGroupType]): A list of stats groups to return.
Returns
async def fetch_aggregated_activity_stats( self, character_id: int, membership_id: int, membership_type: MembershipType | int, /) -> collections.abc.Mapping[str, typing.Any]:
2188    async def fetch_aggregated_activity_stats(
2189        self,
2190        character_id: int,
2191        membership_id: int,
2192        membership_type: enums.MembershipType | int,
2193        /,
2194    ) -> typedefs.JSONObject:
2195        resp = await self._request(
2196            _GET,
2197            f"Destiny2/{int(membership_type)}/Account/{membership_id}/"
2198            f"Character/{character_id}/Stats/AggregateActivityStats/",
2199        )
2200        assert isinstance(resp, dict)
2201        return resp

Fetch aggregated activity stats for a specific membership character.

Parameters
  • character_id (int): The character ID to return the stats for.
  • membership_id (int): The Destiny membership id to return the stats for.
  • membership_type (aiobungie.MembershipType | int): The Destiny membership type to return the stats for.
Returns
async def equip_loadout( self, access_token: str, /, loadout_index: int, character_id: int, membership_type: MembershipType | int) -> None:
2203    async def equip_loadout(
2204        self,
2205        access_token: str,
2206        /,
2207        loadout_index: int,
2208        character_id: int,
2209        membership_type: enums.MembershipType | int,
2210    ) -> None:
2211        response = await self._request(
2212            _POST,
2213            "Destiny2/Actions/Loadouts/EquipLoadout/",
2214            json={
2215                "loadoutIndex": loadout_index,
2216                "characterId": character_id,
2217                "membership_type": int(membership_type),
2218            },
2219            auth=access_token,
2220        )
2221        assert isinstance(response, int)

Equip a loadout. Your character must be in a Social space, Orbit or Offline while performing this operation.

This operation requires MoveEquipDestinyItems OAuth2 scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • loadout_index (int): The index of the loadout to use.
  • character_id (int): The character ID to equip the loadout to.
  • membership_type (aiobungie.MembershipType | int): The membership type of the account.
async def snapshot_loadout( self, access_token: str, /, loadout_index: int, character_id: int, membership_type: MembershipType | int, *, color_hash: int | None = None, icon_hash: int | None = None, name_hash: int | None = None) -> None:
2223    async def snapshot_loadout(
2224        self,
2225        access_token: str,
2226        /,
2227        loadout_index: int,
2228        character_id: int,
2229        membership_type: enums.MembershipType | int,
2230        *,
2231        color_hash: int | None = None,
2232        icon_hash: int | None = None,
2233        name_hash: int | None = None,
2234    ) -> None:
2235        response = await self._request(
2236            _POST,
2237            "Destiny2/Actions/Loadouts/SnapshotLoadout/",
2238            auth=access_token,
2239            json={
2240                "colorHash": color_hash,
2241                "iconHash": icon_hash,
2242                "nameHash": name_hash,
2243                "loadoutIndex": loadout_index,
2244                "characterId": character_id,
2245                "membershipType": int(membership_type),
2246            },
2247        )
2248        assert isinstance(response, int)

Snapshot a loadout with the currently equipped items.

This operation requires MoveEquipDestinyItems OAuth2 scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • loadout_index (int): The index of the loadout to use.
  • character_id (int): The character ID to equip the loadout to.
  • membership_type (aiobungie.MembershipType | int): The membership type of the account.
Other Parameters
  • color_hash (int | None): ...
  • icon_hash (int | None): ...
  • name_hash (int | None): ...
async def update_loadout( self, access_token: str, /, loadout_index: int, character_id: int, membership_type: MembershipType | int, *, color_hash: int | None = None, icon_hash: int | None = None, name_hash: int | None = None) -> None:
2250    async def update_loadout(
2251        self,
2252        access_token: str,
2253        /,
2254        loadout_index: int,
2255        character_id: int,
2256        membership_type: enums.MembershipType | int,
2257        *,
2258        color_hash: int | None = None,
2259        icon_hash: int | None = None,
2260        name_hash: int | None = None,
2261    ) -> None:
2262        response = await self._request(
2263            _POST,
2264            "Destiny2/Actions/Loadouts/UpdateLoadoutIdentifiers/",
2265            auth=access_token,
2266            json={
2267                "colorHash": color_hash,
2268                "iconHash": icon_hash,
2269                "nameHash": name_hash,
2270                "loadoutIndex": loadout_index,
2271                "characterId": character_id,
2272                "membershipType": int(membership_type),
2273            },
2274        )
2275        assert isinstance(response, int)

Update the loadout. Color, Icon and Name.

This operation requires MoveEquipDestinyItems OAuth2 scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • loadout_index (int): The index of the loadout to use.
  • character_id (int): The character ID to equip the loadout to.
  • membership_type (aiobungie.MembershipType | int): The membership type of the account.
Other Parameters
  • color_hash (int | None): The new color hash of the loadout to update.
  • icon_hash (int | None): The new icon hash of the loadout to update.
  • name_hash (int | None): The new name hash of the loadout to update.
async def clear_loadout( self, access_token: str, /, loadout_index: int, character_id: int, membership_type: MembershipType | int) -> None:
2277    async def clear_loadout(
2278        self,
2279        access_token: str,
2280        /,
2281        loadout_index: int,
2282        character_id: int,
2283        membership_type: enums.MembershipType | int,
2284    ) -> None:
2285        response = await self._request(
2286            _POST,
2287            "Destiny2/Actions/Loadouts/ClearLoadout/",
2288            json={
2289                "loadoutIndex": loadout_index,
2290                "characterId": character_id,
2291                "membership_type": int(membership_type),
2292            },
2293            auth=access_token,
2294        )
2295        assert isinstance(response, int)

Clear the identifiers and items of a loadout.

This operation requires MoveEquipDestinyItems OAuth2 scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • loadout_index (int): The index of the loadout to use.
  • character_id (int): The character ID to equip the loadout to.
  • membership_type (aiobungie.MembershipType | int): The membership type of the account.
class RESTPool:
195class RESTPool:
196    """Pool of `RESTClient` instances.
197
198    This allows to create multiple instances of `RESTClient`s that can be acquired
199    which share the same TCP connector.
200
201    Example
202    -------
203    ```py
204    import aiobungie
205
206    client_pool = aiobungie.RESTPool("token", client_id=1234, client_secret='secret')
207
208    # Using a context manager to acquire an instance
209    # from the pool and close it after.
210    async with client_pool.acquire() as rest:
211        ...
212    ```
213
214    Parameters
215    ----------
216    token : `str`
217        A valid application token from Bungie's developer portal.
218
219    Other Parameters
220    ----------------
221    max_retries : `int`
222        The max retries number to retry if the request hit a `5xx` status code.
223    client_secret : `str | None`
224        An optional application client secret,
225        This is only needed if you're fetching OAuth2 tokens with this client.
226    client_id : `int | None`
227        An optional application client id,
228        This is only needed if you're fetching OAuth2 tokens with this client.
229    debug : `bool | str`
230        Whether to enable logging responses or not.
231
232    Logging Levels
233    --------------
234    * `False`: This will disable logging.
235    * `True`: This will set the level to `DEBUG` and enable logging minimal information.
236    Like the response status, route, taken time and so on.
237    * `"TRACE" | aiobungie.TRACE`: This will log the response headers along with the minimal information.
238    """
239
240    __slots__ = (
241        "_token",
242        "_max_retries",
243        "_client_secret",
244        "_client_id",
245        "_metadata",
246        "_enable_debug",
247        "_client_session",
248        "_loads",
249        "_dumps",
250    )
251
252    # Looks like mypy doesn't like this.
253    if typing.TYPE_CHECKING:
254        _enable_debug: typing.Literal["TRACE"] | bool | int
255
256    def __init__(
257        self,
258        token: str,
259        /,
260        *,
261        client_secret: str | None = None,
262        client_id: int | None = None,
263        client_session: aiohttp.ClientSession | None = None,
264        dumps: typedefs.Dumps = helpers.dumps,
265        loads: typedefs.Loads = helpers.loads,
266        max_retries: int = 4,
267        debug: typing.Literal["TRACE"] | bool | int = False,
268    ) -> None:
269        self._client_secret = client_secret
270        self._client_id = client_id
271        self._token = token
272        self._max_retries = max_retries
273        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
274        self._enable_debug = debug
275        self._client_session = client_session
276        self._loads = loads
277        self._dumps = dumps
278
279    @property
280    def client_id(self) -> int | None:
281        return self._client_id
282
283    @property
284    def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]:
285        """Pool's Metadata. This is different from client instance metadata."""
286        return self._metadata
287
288    @typing.final
289    def acquire(self) -> RESTClient:
290        """Acquires a new `RESTClient` instance from this pool.
291
292        Returns
293        -------
294        `RESTClient`
295            An instance of a `RESTClient`.
296        """
297        return RESTClient(
298            self._token,
299            client_secret=self._client_secret,
300            client_id=self._client_id,
301            loads=self._loads,
302            dumps=self._dumps,
303            max_retries=self._max_retries,
304            debug=self._enable_debug,
305            client_session=self._client_session,
306        )

Pool of RESTClient instances.

This allows to create multiple instances of RESTClients that can be acquired which share the same TCP connector.

Example
import aiobungie

client_pool = aiobungie.RESTPool("token", client_id=1234, client_secret='secret')

# Using a context manager to acquire an instance
# from the pool and close it after.
async with client_pool.acquire() as rest:
    ...
Parameters
  • token (str): A valid application token from Bungie's developer portal.
Other Parameters
  • max_retries (int): The max retries number to retry if the request hit a 5xx status code.
  • client_secret (str | None): An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client.
  • client_id (int | None): An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client.
  • debug (bool | str): Whether to enable logging responses or not.
Logging Levels
  • False: This will disable logging.
  • True: This will set the level to DEBUG and enable logging minimal information. Like the response status, route, taken time and so on.
  • "TRACE" | TRACE: This will log the response headers along with the minimal information.
RESTPool( token: str, /, *, client_secret: str | None = None, client_id: int | None = None, client_session: aiohttp.client.ClientSession | None = None, dumps: collections.abc.Callable[[collections.abc.Mapping[str, typing.Any] | collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]]], bytes] = <function dumps>, loads: collections.abc.Callable[[str | bytes], collections.abc.Sequence[collections.abc.Mapping[str, typing.Any]] | collections.abc.Mapping[str, typing.Any]] = <function loads>, max_retries: int = 4, debug: Union[Literal['TRACE'], bool, int] = False)
256    def __init__(
257        self,
258        token: str,
259        /,
260        *,
261        client_secret: str | None = None,
262        client_id: int | None = None,
263        client_session: aiohttp.ClientSession | None = None,
264        dumps: typedefs.Dumps = helpers.dumps,
265        loads: typedefs.Loads = helpers.loads,
266        max_retries: int = 4,
267        debug: typing.Literal["TRACE"] | bool | int = False,
268    ) -> None:
269        self._client_secret = client_secret
270        self._client_id = client_id
271        self._token = token
272        self._max_retries = max_retries
273        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
274        self._enable_debug = debug
275        self._client_session = client_session
276        self._loads = loads
277        self._dumps = dumps
client_id: int | None
279    @property
280    def client_id(self) -> int | None:
281        return self._client_id
metadata: collections.abc.MutableMapping[typing.Any, typing.Any]
283    @property
284    def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]:
285        """Pool's Metadata. This is different from client instance metadata."""
286        return self._metadata

Pool's Metadata. This is different from client instance metadata.

@typing.final
def acquire(self) -> RESTClient:
288    @typing.final
289    def acquire(self) -> RESTClient:
290        """Acquires a new `RESTClient` instance from this pool.
291
292        Returns
293        -------
294        `RESTClient`
295            An instance of a `RESTClient`.
296        """
297        return RESTClient(
298            self._token,
299            client_secret=self._client_secret,
300            client_id=self._client_id,
301            loads=self._loads,
302            dumps=self._dumps,
303            max_retries=self._max_retries,
304            debug=self._enable_debug,
305            client_session=self._client_session,
306        )

Acquires a new RESTClient instance from this pool.

Returns
@typing.final
class Race(builtins.int, aiobungie.Enum):
485@typing.final
486class Race(int, Enum):
487    """An Enum for Destiny races."""
488
489    HUMAN = 0
490    AWOKEN = 1
491    EXO = 2
492    UNKNOWN = 3

An Enum for Destiny races.

HUMAN = <Race.HUMAN: 0>
AWOKEN = <Race.AWOKEN: 1>
EXO = <Race.EXO: 2>
UNKNOWN = <Race.UNKNOWN: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Raid(builtins.int, aiobungie.Enum):
132@typing.final
133class Raid(int, Enum):
134    """An Enum for all available raids in Destiny 2."""
135
136    DSC = 910380154
137    """Deep Stone Crypt"""
138
139    LW = 2122313384
140    """Last Wish"""
141
142    VOG = 3881495763
143    """Normal Valut of Glass"""
144
145    GOS = 3458480158
146    """Garden Of Salvation"""

An Enum for all available raids in Destiny 2.

DSC = <Raid.DSC: 910380154>

Deep Stone Crypt

LW = <Raid.LW: 2122313384>

Last Wish

VOG = <Raid.VOG: 3881495763>

Normal Valut of Glass

GOS = <Raid.GOS: 3458480158>

Garden Of Salvation

Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class RateLimitedError(aiobungie.HTTPError):
250@attrs.define(auto_exc=True)
251class RateLimitedError(HTTPError):
252    """Raised when too many request status code is returned."""
253
254    http_status: http.HTTPStatus = attrs.field(
255        default=http.HTTPStatus.TOO_MANY_REQUESTS, init=False
256    )
257    """The request response http status."""
258
259    url: typedefs.StrOrURL
260    """The URL/endpoint caused this error."""
261
262    body: typing.Any
263    """The response body."""
264
265    retry_after: float = attrs.field(default=0.0)
266    """The amount of seconds you need to wait before retrying to requests."""
267
268    message: str = attrs.field(init=False)
269    """A Bungie human readable message describes the cause of the error."""
270
271    # Type Ignore: attrs provide a `.default` setter on its attribs to allow
272    # changing the default value.
273    @message.default  # pyright: ignore
274    def _(self) -> str:
275        return f"You're ratelimited for {self.retry_after}, Endpoint: {self.url}. Slow down!"
276
277    def __str__(self) -> str:
278        return self.message

Raised when too many request status code is returned.

RateLimitedError(url: Union[str, yarl.URL], body: Any, retry_after: float = 0.0)
2def __init__(self, url, body, retry_after=attr_dict['retry_after'].default):
3    self.http_status = attr_dict['http_status'].default
4    self.url = url
5    self.body = body
6    self.retry_after = retry_after
7    self.message = __attr_factory_message(self)
8    BaseException.__init__(self, self.url,self.body,self.retry_after)

Method generated by attrs for class RateLimitedError.

http_status: http.HTTPStatus

The request response http status.

url: Union[str, yarl.URL]

The URL/endpoint caused this error.

body: Any

The response body.

retry_after: float

The amount of seconds you need to wait before retrying to requests.

message: str

A Bungie human readable message describes the cause of the error.

Inherited Members
builtins.BaseException
with_traceback
args
@typing.final
class RecordState(aiobungie.Flag):
50@typing.final
51class RecordState(enums.Flag):
52    """An enum for records component states."""
53
54    NONE = 0
55    REDEEMED = 1 << 0
56    UNAVAILABLE = 1 << 1
57    OBJECTIVE_NOT_COMPLETED = 1 << 2
58    OBSCURED = 1 << 3
59    INVISIBLE = 1 << 4
60    ENTITLEMENT_UNOWNED = 1 << 5
61    CAN_EQUIP_TITLE = 1 << 6

An enum for records component states.

NONE = <RecordState.NONE: 0>
REDEEMED = <RecordState.REDEEMED: 1>
UNAVAILABLE = <RecordState.UNAVAILABLE: 2>
OBJECTIVE_NOT_COMPLETED = <RecordState.OBJECTIVE_NOT_COMPLETED: 4>
OBSCURED = <RecordState.OBSCURED: 8>
INVISIBLE = <RecordState.INVISIBLE: 16>
ENTITLEMENT_UNOWNED = <RecordState.ENTITLEMENT_UNOWNED: 32>
CAN_EQUIP_TITLE = <RecordState.CAN_EQUIP_TITLE: 64>
Inherited Members
Flag
name
value
@typing.final
class Relationship(builtins.int, aiobungie.Enum):
680@typing.final
681class Relationship(int, Enum):
682    """An enum for bungie friends relationship types."""
683
684    UNKNOWN = 0
685    FRIEND = 1
686    INCOMING_REQUEST = 2
687    OUTGOING_REQUEST = 3

An enum for bungie friends relationship types.

UNKNOWN = <Relationship.UNKNOWN: 0>
FRIEND = <Relationship.FRIEND: 1>
INCOMING_REQUEST = <Relationship.INCOMING_REQUEST: 2>
OUTGOING_REQUEST = <Relationship.OUTGOING_REQUEST: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class ResponseError(aiobungie.HTTPException):
245@attrs.define(auto_exc=True)
246class ResponseError(HTTPException):
247    """Exception for other HTTP response errors."""

Exception for other HTTP response errors.

ResponseError( *, error_code: int, http_status: http.HTTPStatus, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, http_status, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.http_status = http_status
 5    self.throttle_seconds = throttle_seconds
 6    self.url = url
 7    self.body = body
 8    self.headers = headers
 9    self.message = message
10    self.error_status = error_status
11    self.message_data = message_data
12    BaseException.__init__(self, self.error_code,self.http_status,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class ResponseError.

Inherited Members
HTTPException
error_code
http_status
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
args
@typing.final
class Stat(builtins.int, aiobungie.Enum):
507@typing.final
508class Stat(int, Enum):
509    """An Enum for Destiny 2 character stats."""
510
511    NONE = 0
512    MOBILITY = 2996146975
513    RESILIENCE = 392767087
514    RECOVERY = 1943323491
515    DISCIPLINE = 1735777505
516    INTELLECT = 144602215
517    STRENGTH = 4244567218
518    LIGHT_POWER = 1935470627

An Enum for Destiny 2 character stats.

NONE = <Stat.NONE: 0>
MOBILITY = <Stat.MOBILITY: 2996146975>
RESILIENCE = <Stat.RESILIENCE: 392767087>
RECOVERY = <Stat.RECOVERY: 1943323491>
DISCIPLINE = <Stat.DISCIPLINE: 1735777505>
INTELLECT = <Stat.INTELLECT: 144602215>
STRENGTH = <Stat.STRENGTH: 4244567218>
LIGHT_POWER = <Stat.LIGHT_POWER: 1935470627>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
TRACE = 5
@typing.final
class TierType(builtins.int, aiobungie.Enum):
622@typing.final
623class TierType(int, Enum):
624    """An enum for a Destiny 2 item tier type."""
625
626    UNKNOWN = 0
627    CURRENCY = 1
628    BASIC = 2
629    COMMON = 3
630    RARE = 4
631    SUPERIOR = 5
632    EXOTIC = 6

An enum for a Destiny 2 item tier type.

UNKNOWN = <TierType.UNKNOWN: 0>
CURRENCY = <TierType.CURRENCY: 1>
BASIC = <TierType.BASIC: 2>
COMMON = <TierType.COMMON: 3>
RARE = <TierType.RARE: 4>
SUPERIOR = <TierType.SUPERIOR: 5>
EXOTIC = <TierType.EXOTIC: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class TransferStatus(aiobungie.Flag):
732@typing.final
733class TransferStatus(Flag):
734    """An enum for items transfer statuses."""
735
736    CAN_TRANSFER = 0
737    """The item can be transferred."""
738    IS_EQUIPPED = 1 << 0
739    """You can't transfer since the item is equipped."""
740    NOT_TRASNFERRABLE = 1 << 1
741    """This item can not be transferred."""
742    COULD_BE_TRANSFERRED = 1 << 2
743    """You can transfer the item. But the place you're trying to put it at has no space for it."""

An enum for items transfer statuses.

CAN_TRANSFER = <TransferStatus.CAN_TRANSFER: 0>

The item can be transferred.

IS_EQUIPPED = <TransferStatus.IS_EQUIPPED: 1>

You can't transfer since the item is equipped.

NOT_TRASNFERRABLE = <TransferStatus.NOT_TRASNFERRABLE: 2>

This item can not be transferred.

COULD_BE_TRANSFERRED = <TransferStatus.COULD_BE_TRANSFERRED: 4>

You can transfer the item. But the place you're trying to put it at has no space for it.

Inherited Members
Flag
name
value
@attrs.define(auto_exc=True)
class Unauthorized(aiobungie.HTTPException):
154@attrs.define(auto_exc=True)
155class Unauthorized(HTTPException):
156    """An exception that's raised when trying to make unauthorized call to a resource and it returns 404."""
157
158    http_status: http.HTTPStatus = attrs.field(
159        default=http.HTTPStatus.UNAUTHORIZED, init=False
160    )

An exception that's raised when trying to make unauthorized call to a resource and it returns 404.

Unauthorized( *, error_code: int, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.throttle_seconds = throttle_seconds
 5    self.url = url
 6    self.body = body
 7    self.headers = headers
 8    self.message = message
 9    self.error_status = error_status
10    self.message_data = message_data
11    self.http_status = attr_dict['http_status'].default
12    BaseException.__init__(self, self.error_code,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class Unauthorized.

http_status: http.HTTPStatus

The request response http status.

Inherited Members
HTTPException
error_code
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
args
@typing.final
class ValueUIStyle(builtins.int, aiobungie.Enum):
74@typing.final
75class ValueUIStyle(int, enums.Enum):
76    AUTOMATIC = 0
77    FRACTION = 1
78    CHECK_BOX = 2
79    PERCENTAGE = 3
80    DATETIME = 4
81    FRACTION_FLOAT = 5
82    INTEGER = 6
83    TIME_DURATION = 7
84    HIDDEN = 8
85    MULTIPLIER = 9
86    GREEN_PIPS = 10
87    RED_PIPS = 11
88    EXPLICIT_PERCENTAGE = 12
89    RAW_FLOAT = 13
90    LEVEL_AND_REWARD = 14

An enumeration.

AUTOMATIC = <ValueUIStyle.AUTOMATIC: 0>
FRACTION = <ValueUIStyle.FRACTION: 1>
CHECK_BOX = <ValueUIStyle.CHECK_BOX: 2>
PERCENTAGE = <ValueUIStyle.PERCENTAGE: 3>
DATETIME = <ValueUIStyle.DATETIME: 4>
FRACTION_FLOAT = <ValueUIStyle.FRACTION_FLOAT: 5>
INTEGER = <ValueUIStyle.INTEGER: 6>
TIME_DURATION = <ValueUIStyle.TIME_DURATION: 7>
HIDDEN = <ValueUIStyle.HIDDEN: 8>
MULTIPLIER = <ValueUIStyle.MULTIPLIER: 9>
GREEN_PIPS = <ValueUIStyle.GREEN_PIPS: 10>
RED_PIPS = <ValueUIStyle.RED_PIPS: 11>
EXPLICIT_PERCENTAGE = <ValueUIStyle.EXPLICIT_PERCENTAGE: 12>
RAW_FLOAT = <ValueUIStyle.RAW_FLOAT: 13>
LEVEL_AND_REWARD = <ValueUIStyle.LEVEL_AND_REWARD: 14>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Vendor(builtins.int, aiobungie.Enum):
229@typing.final
230class Vendor(int, Enum):
231    """An Enum for all available vendors in Destiny 2."""
232
233    ZAVALA = 69482069
234    XUR = 2190858386
235    BANSHE = 672118013
236    SPIDER = 863940356
237    SHAXX = 3603221665
238    KADI = 529635856
239    """Postmaster exo."""
240    YUNA = 1796504621
241    """Asia servers only."""
242    EVERVERSE = 3361454721
243    AMANDA = 460529231
244    """Amanda holiday"""
245    CROW = 3611983588
246    HAWTHORNE = 3347378076
247    ADA1 = 350061650
248    DRIFTER = 248695599
249    IKORA = 1976548992
250    SAINT = 765357505
251    """Saint-14"""
252    ERIS_MORN = 1616085565
253    SHAW_HAWN = 1816541247
254    """COSMODROME Guy"""
255    VARIKS = 2531198101

An Enum for all available vendors in Destiny 2.

ZAVALA = <Vendor.ZAVALA: 69482069>
XUR = <Vendor.XUR: 2190858386>
BANSHE = <Vendor.BANSHE: 672118013>
SPIDER = <Vendor.SPIDER: 863940356>
SHAXX = <Vendor.SHAXX: 3603221665>
KADI = <Vendor.KADI: 529635856>

Postmaster exo.

YUNA = <Vendor.YUNA: 1796504621>

Asia servers only.

EVERVERSE = <Vendor.EVERVERSE: 3361454721>
AMANDA = <Vendor.AMANDA: 460529231>

Amanda holiday

CROW = <Vendor.CROW: 3611983588>
HAWTHORNE = <Vendor.HAWTHORNE: 3347378076>
ADA1 = <Vendor.ADA1: 350061650>
DRIFTER = <Vendor.DRIFTER: 248695599>
IKORA = <Vendor.IKORA: 1976548992>
SAINT = <Vendor.SAINT: 765357505>

Saint-14

ERIS_MORN = <Vendor.ERIS_MORN: 1616085565>
SHAW_HAWN = <Vendor.SHAW_HAWN: 1816541247>

COSMODROME Guy

VARIKS = <Vendor.VARIKS: 2531198101>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class WeaponType(builtins.int, aiobungie.Enum):
521@typing.final
522class WeaponType(int, Enum):
523    """Enums for The three Destiny Weapon Types"""
524
525    NONE = 0
526    KINETIC = 1498876634
527    ENERGY = 2465295065
528    POWER = 953998645

Enums for The three Destiny Weapon Types

NONE = <WeaponType.NONE: 0>
KINETIC = <WeaponType.KINETIC: 1498876634>
ENERGY = <WeaponType.ENERGY: 2465295065>
POWER = <WeaponType.POWER: 953998645>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
annotations = _Feature((3, 7, 0, 'beta', 1), (3, 11, 0, 'alpha', 0), 16777216)
def iter( iterable: collections.abc.Iterable[~Item]) -> Iterator[~Item]:
616def iter(
617    iterable: collections.Iterable[Item],
618) -> Iterator[Item]:
619    """Transform an iterable into an flat iterator.
620
621    Example
622    -------
623    ```py
624    sequence = (1,2,3)
625    for item in aiobungie.iter(sequence).reversed():
626        print(item)
627    # 3
628    # 2
629    # 1
630    ```
631
632    Parameters
633    ----------
634    iterable: `typing.Iterable[Item]`
635        The iterable to convert.
636
637    Raises
638    ------
639    `StopIteration`
640        If no elements are left in the iterator.
641    """
642    return Iterator(iterable)

Transform an iterable into an flat iterator.

Example
sequence = (1,2,3)
for item in aiobungie.iter(sequence).reversed():
    print(item)
# 3
# 2
# 1
Parameters
  • iterable (typing.Iterable[Item]): The iterable to convert.
Raises
  • StopIteration: If no elements are left in the iterator.
async def panic( response: aiohttp.client_reqrep.ClientResponse) -> HTTPError:
281async def panic(response: aiohttp.ClientResponse) -> HTTPError:
282    """Immediately raise an exception based on the response."""
283
284    # Bungie get funky and return HTML instead of JSON when making an authorized
285    # request with a dummy access token. We could technically read the page content
286    # but that's Bungie's fault for not returning a JSON response.
287    if response.content_type != "application/json":
288        raise HTTPError(
289            message=f"Expected JSON response, Got {response.content_type}, "
290            f"{response.real_url.human_repr()}",
291            http_status=http.HTTPStatus(response.status),
292        )
293
294    body: collections.Mapping[str, typing.Any] = helpers.loads(await response.read())  # type: ignore
295    message: str = body.get("Message", "UNDEFINED_MESSAGE")
296    error_status: str = body.get("ErrorStatus", "UNDEFINED_ERROR_STATUS")
297    message_data: dict[str, str] = body.get("MessageData", {})
298    throttle_seconds: int = body.get("ThrottleSeconds", 0)
299    error_code: int = body.get("ErrorCode", 0)
300
301    # Standard HTTP status.
302    match response.status:
303        case http.HTTPStatus.NOT_FOUND:
304            return NotFound(
305                message=message,
306                error_code=error_code,
307                throttle_seconds=throttle_seconds,
308                url=str(response.real_url),
309                body=body,
310                headers=response.headers,
311                error_status=error_status,
312                message_data=message_data,
313            )
314
315        case http.HTTPStatus.FORBIDDEN:
316            return Forbidden(
317                message=message,
318                error_code=error_code,
319                throttle_seconds=throttle_seconds,
320                url=str(response.real_url),
321                body=body,
322                headers=response.headers,
323                error_status=error_status,
324                message_data=message_data,
325            )
326
327        case http.HTTPStatus.UNAUTHORIZED:
328            return Unauthorized(
329                message=message,
330                error_code=error_code,
331                throttle_seconds=throttle_seconds,
332                url=str(response.real_url),
333                body=body,
334                headers=response.headers,
335                error_status=error_status,
336                message_data=message_data,
337            )
338
339        case http.HTTPStatus.BAD_REQUEST:
340            # Membership needs to be alone.
341            if error_status == "InvalidParameters":
342                return MembershipTypeError(
343                    message=message,
344                    body=body,
345                    headers=response.headers,
346                    url=str(response.url),
347                    membership_type=message_data["membershipType"],
348                    required_membership=message_data["membershipInfo.membershipType"],
349                    membership_id=int(message_data["membershipId"]),
350                )
351            return BadRequest(
352                message=message,
353                body=body,
354                headers=response.headers,
355                url=str(response.url),
356            )
357        case _:
358            status = http.HTTPStatus(response.status)
359
360            if 400 <= status < 500:
361                return ResponseError(
362                    message=message,
363                    error_code=error_code,
364                    throttle_seconds=throttle_seconds,
365                    url=str(response.real_url),
366                    body=body,
367                    headers=response.headers,
368                    error_status=error_status,
369                    message_data=message_data,
370                    http_status=status,
371                )
372
373            # Need to self handle ~5xx errors
374            elif 500 <= status < 600:
375                # No API key or method requires OAuth2 most likely.
376                if error_status in {
377                    "ApiKeyMissingFromRequest",
378                    "WebAuthRequired",
379                    "ApiInvalidOrExpiredKey",
380                    "AuthenticationInvalid",
381                    "AuthorizationCodeInvalid",
382                }:
383                    return Unauthorized(
384                        message=message,
385                        error_code=error_code,
386                        throttle_seconds=throttle_seconds,
387                        url=str(response.real_url),
388                        body=body,
389                        headers=response.headers,
390                        error_status=error_status,
391                        message_data=message_data,
392                    )
393
394                # Anything contains not found.
395                elif (
396                    "NotFound" in error_status
397                    or error_status == "UserCannotFindRequestedUser"
398                ):
399                    return NotFound(
400                        message=message,
401                        error_code=error_code,
402                        throttle_seconds=throttle_seconds,
403                        url=str(response.real_url),
404                        body=body,
405                        headers=response.headers,
406                        error_status=error_status,
407                        message_data=message_data,
408                    )
409
410                # Other 5xx errors.
411                else:
412                    return InternalServerError(
413                        message=message,
414                        error_code=error_code,
415                        throttle_seconds=throttle_seconds,
416                        url=str(response.real_url),
417                        body=body,
418                        headers=response.headers,
419                        error_status=error_status,
420                        message_data=message_data,
421                        http_status=status,
422                    )
423            # Something else.
424            else:
425                return HTTPException(
426                    message=message,
427                    error_code=error_code,
428                    throttle_seconds=throttle_seconds,
429                    url=str(response.real_url),
430                    body=body,
431                    headers=response.headers,
432                    error_status=error_status,
433                    message_data=message_data,
434                    http_status=status,
435                )

Immediately raise an exception based on the response.

def stringify_headers(headers: collections.abc.Mapping[str, typing.Any]) -> str:
438def stringify_headers(headers: collections.Mapping[str, typing.Any]) -> str:
439    return (
440        "{ \n"
441        + "\n".join(  # noqa: W503
442            f"{f'   {key}'}: {value}"
443            if key
444            not in {
445                "Authorization",
446                "X-API-KEY",
447                "client_secret",
448                "client_id",
449                "access_token",
450                "refresh_token",
451            }
452            else f"   {key}: REDACTED_TOKEN"
453            for key, value in headers.items()
454        )
455        + "\n}"  # noqa: W503
456    )